Full Stack Airbnb Clone with Next.js 13 App Router: React, Tailwind, Prisma, MongoDB, NextAuth 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey there my name is Antonio and Welcome to our exciting tutorial where we'll be building a full stack Airbnb clone with some of the latest and greatest Technologies with next 13 app directory we'll create a highly optimized application that is easy to navigate and lightning fast to load we'll also be leveraging server components to fetch data directly from the database creating a seamless user experience additionally we'll be using client components to build out the UI ensuring a smooth and responsive design with Tailwind CSS we'll be creating a sleek and modern look for our Airbnb clone complete with animations and effects that bring the site to life our design is fully responsive ensuring that it looks great on all devices from desktop to mobile we'll be implementing credential authentication as well as Google and GitHub authentication for a more seamless login process we will also be using cloudinary CDN for image uploads and client phone validation and handling using react hook form to ensure smooth operation we'll be using react toast for server error handling and react date range for calendars we'll also have page loading and empty States and a booking reservation system with guest and owner reservation cancellation our Airbnb clone also features creation and deletion of properties Advanced pricing calculation and an advanced search algorithm that filters results by category date range map location number of guests rooms and bathrooms for example you can filter out properties that have a reservation in your desired date range to travel but we're not stopping there our Airbnb clone also includes a favorite system and shareable URL filters this means that if you select a category location and date range you can share the URL with a logged out friend in another browser and they will see the same results as you so buckle up and get ready to dive into building a full stack Airbnb clone with next 13 app directory server components client components Tailwind design and all the amazing features we've mentioned and in the end I'm going to show you how you can deploy all of that on Virtual so in this part of the tutorial we're going to set up our environment so go into the terminal and write in the following command MPX create next app typescript name your project in my case it's going to be Airbnb bash video or the sland option select yes for the source directory select now and for the experimental app directory you're going to select yes because we are going to be building this Airbnb clone using the newest app router for the import Alias you can just press enter and that is going to default to an add sign and now just wait a moment for all of these packages to install great now let's go ahead and let's open our project so I'm going to select Airbnb video because that's what I named my project and now I want you to go into app folder right here to see the new structure instead of our usual underscore app and underscore document we now have a single file called layout.tsx and instead of the usual index.dsx we now have page.vsx file right here so let's go back into our terminal and let's run the following command to start the project npm run depth and you can go into your browser now to localhost 3000 and just load the new app and we'll greet it with the next 13 welcome screen great I'm going to close the terminal for now and I want you to head back into app page that's the SX file right here and let's clean this up so you can select everything in the return function and you can just press delete and instead of that we're going to write a div which is guys just going to say hello Airbnb and just like that you can see that our website updated to say hello Airbnb right here and I'm also going to remove all of the Imports right here like this all right and now I want you to go into app layout.tsx file right here and the first things you will probably notice is this metadata constant so this is a reserved constant in next 13 layout file where you can control your title and description and some other options you can probably see or maybe you can't but my tab name in my browser is create next app and that is controlled from here so if I change it to Airbnb and look again it's going to change to Airbnb and I'm also going to change my description to Airbnb clone great and now I want to show you how easy it is to change the font with next font package so go ahead and Import in curly brackets in anito from next slash font slash Google and now what you're able to do with that is create a constant called font write nunito and open an object inside parenthesis here and just add a subset so subsets is going to be an array which is going to hold Latin like this and what this font does now is exposes the class name which we can give to our body element so go ahead and write class name onto that class name and if you go back into your application once it reloads you see we have a new font great next thing I want to clean up is the app globals.css file so you can go inside you can select everything and you can just press delete and now you will get a plain white page like this and also remove page.module.css as well great now the next thing I want to do is I want to set up Tailwind so go ahead and open your terminal again and you can shut down the application for now and right here I have the Tailwind CSS documentation ready so first things first we have to install some packages so go ahead and run npm install Dash D Tailwind CSS host CSS and Auto prefixer great and now you can go ahead and run the following command npx Tailwind CSS init Dash e and that created two new files let's go ahead and let's take a look at them so we have the new file post CSS config and tailwind.config.js we're going to focus on tailwind.config.js so go ahead and open tailwind.config.js right here and next thing we have to do is we have to add add our template paths so go ahead and write it the following dot slash app slash Asterix Asterix slash Asterix dot curly brackets JS comma PS comma jsx comma TSX and you can just copy that two times for the second one you're gonna write pages and for the last one you're going to write components now technically we don't need anything except app but if you want to play around in the pages folder which you still can and the components folder which you absolutely can uh I want to leave this here just so you don't lose your Styles it's not going to hurt anyone great and last thing we have to do is we have to go into our app again right here in the app folder and select globals.css which we just recently cleaned and inside we have to add our Tailwind direct types so go ahead and run add Tailwind days at Tailwind components and have Tailwind beetles my apologies utilities great you can save this file and now finally you can go into your terminal again and you can run npm run Dev and I'm gonna go back into my localhost 3000. and just click refresh once the page has compiled you're probably not going to see too much of a difference but you will probably notice that the padding around our text has been removed and that is because these direct navs have allowed us to reset our CSS great and now just to test whatever we've done this correctly let's close everything and let's go into app into page.dsx right here and let's add a CSS Tailwind class to this div it's a right class name and just write text Dash rows-500 and tax Dash 2XL for example save the file and as you can see we have enlarged our text and we have added a nice rose color right here great so our Tailwind has been successfully set up last thing I want you to do before we move on is go back into your globals.css file right here and just write the following so I want you to write HTML comma body and root just like this and inside of that you're going to write height 100 you're probably not going to notice any changes but I wrote this just because of all of the elements we are going to need in the future great job you successfully set up the environment so we're going to continue with our tutorial and in this part we're going to add our navigation bar so I'm just gonna unzoom a little bit because I zoomed a lot last time so let's go back and let's go into our app folder and select layout the TSX right here and let's collapse this children inside this body element and above them let's write nav bar like this and if we save we're going to get an error because navbar is not defined so what I want you to do is I want you to go into app folder and create a new folder called components now important thing I want to mention you no longer have to write your components inside the components folder you can write them wherever you want but for the ease of understanding and I think this structure is familiar with everyone I'm gonna keep writing them in the components folder inside the app but you can name this folder whatever you want but in my case it's going to be components and inside these components I'm going to create a new folder called navbar so I can separate my components in a better way and in this number component I'm going to create a new file called navbar.tsx I'm going to name the component navbar and for now all it's going to do is return a div which is going to say I am a navbar like this and now we can go back into our layout.tsx file and we can import the navbar like this great and if you save you should lose the error all right now let's go back into our navbar component and let's add some Styles so the first div is going to have a class name of text W full VG plus white z-10 and Shadow SM like this and you can see that we have our little number right here with a small Shadow and our body has currently disappeared that is because we have put a fixed position on this now bar but we're going to fix that later with some padding good now inside create another div which is going to have the following Styles so class name again and we're going to write py4 and Border Dash B Dash square brackets 1 pixel and that is going to enforce how you see this number you can see how we can clearly distinct between the body and the nav bar here at the top great and now inside of that div we're going to create another component so right now all I want to do is write container like this and for now you can just save and you'll get an error because container does not exist so let's go ahead and let's go into our components so not in the navbar but into components and create a new file called container.dsx so your structure should should look something like this so you have the app folder you have the components folder and inside of components you have the navbar folder where our navbar.tsx is but outside navbar but still inside components you have the container.ds X because container is going to be reused across the app but all of these components we write in navbar are only going to be for navbar great so let's go into container.dsx and let's do the same thing let's name it container right here and let's just write div container like this and now let's go back into our navbar.tsx and let's just import this like that and now if you save the error is gone and you can see the container element right here so I'm gonna head back into the container right here and let's write an interface here interface container props is going to be children which is a type of react.react node and now we can assign these props to this element so react functional component container props and you can just extract the children from the props like this and then you can replace the container with children like this and now we have uh we no longer see our text but if we go back into navbar.dsx and write anything here we can literally write anything like this and save you can see that it is passed as our children to The Container right here great and one thing I want to do before we move on is I want to mark this component as a client component this is something we have to do when we work with the app folder because every component and Page we create inside the app folder is a server component by default and there can be hydration problems if you mix and match Imports with client and server components but don't worry I'm going to show you how to do this in a proper way and of course we are going to use server components to fetch our data directly from the database but the container for now is going to be a styling component so let's just write use client at the top because we are going to import this container in other client components so it's important that this is also a client component great and now let's just collapse this children here because we are going to write some class names in this div so let's write class name and let's write Max W square brackets 2520 pixels below that MX Auto and then we are going to write some viewport options so on extra large screens the PX padding is going to be 20 on MD PX is going to be 10 on small PX is going to be 2 and on extra small PX is going to be 4. there and you can see how now we have this a bit of spacing that we have created great and now we can go back into our navbar component right here and let's write some other elements here so let's start by writing another div inside and let's give it the following class names blacks black slash row item slash Center justify Dash between Gap Dash 3 and MD gap-0 just like that okay and now inside I'm gonna add a logo component so inside this newly created tip just write logo like this with the capital L and if you save of course we get an error and now let's create a new component inside components inside navbar folder so create a new file called logo.bsx just like that great and you can already guess that this component is also going to be needed also going to be needed to be a client component so at the top we're going to write use client like this and the next thing we're going to do is we're going to import image from next image so import image from next slash image and we are also going to import our router but be careful how I'm going to do this so import use router and you might be thinking well I already know this it's from next slash router but in the next 13 we're using the new package so write import use router from next slash navigation and now let's write our components so constant logo it's going to use the router so just write used router like this and it's going to return an image element now let's add some attributes to this image element the alt is going to be logo the class name is going to be hidden on small or mobile devices on MD is going to turn into a block device and cursor is going to be pointer great now let's add the height which is 100 and the width which is also 100 and last thing we have left is the source source is going to be slash Images slash logo.png and if you save and go back into navbar.tsx and import this logo component here let's import logo from logo like this and of course make sure you export default this logo component so go back into logo TSX and Export vehicle logo like this save the file and save navbar make sure you import logo from logo and put the logo here you can see we have a broken image here and that is because we have not added an image in our public file so you can either find the Airbnb logo on Google or you can go into the link in the description and go into my GitHub repository here and go into public folder and go into images and click on logo.png and let's just download this file so I'm gonna right click and save as and in this public folder right here I'm going to create a new folder called images and I'm going to drag and drop the newly downloaded logo inside and make sure it's named logo.png like that and if you go back into Airbnb and click refresh you can see that we have our nice little logo in the left corner great let's close everything so we can learn to navigate through this again so go to app components navbar and navbar again and let's continue working on this so the next thing that this Airbnb nav bar is going to have is the search filters so go ahead and write search like this again if we save we're going to get an error let's go into nav bar and create a new file search.tsx inside and let's just name it search and as always let's just return a div let's go back into navbar and let's import search from search and save and that should fix the error great now let's go into our search.dsx and before we move on I want to install a package here which we are going to need so shut down the app for now and write npm install react Dash icons great after it's installed you can run npm run Dev again and I'm just going to close this terminal and now let's style this a little bit so let's write the following classes here last name order Dash square brackets one pixel W Dash cool MDW Auto py-2 rounded Dash full Shadow SM hover Shadow MD transition and cursor Dash pointer right now we probably won't see anything because we have not added any elements inside this div to actually expand it but before we move on we have to Define this search element as a client component so go to the top and right use the client great now let's go back into styling our search so inside this div create another dip with the following class names last name is going to be Plex Black slash row items Dash Center and justify Dash between and inside of that create another div we're just going to have the following class names text SM font semi bold and px-6 and inside of that you can just write anywhere and make sure you refresh your page after you remember we install the package react icons and then we run npm run Dev so just refresh your page to make sure you can see the newest code here all right and now we can see this anywhere box right here great now let's add some more items here so just uh below this anywhere box create another div and give it the following class names hidden SM block text Dash SM font semi bold px-6 border Dash x square brackets 1 pixel Flex dash one and fax Dash Center and inside of that you can just write any week like this and you can see we now have two elements anywhere and any week we have one more element to write so go ahead and just below this and a weak box create another box and let's give you the following classes text Dash SM el-6 br-2 text Dash gray Dash 600 Plex Black slash row item slash Center and GAP Dash three great and inside of that we're going to create two more divs so create another div and give it a class name of hidden Ascent block like this and just write uh add guests like this and you can see now we have ADD guests as the third element but it's a little close to the end right here and I did that on purpose because we're going to add an icon here so create another div inside but just below this add guests div and style it as the following class name 3-2 BG Dash rows Dash 500 rounded Dash full and text White and what we're going to add inside is an icon so go ahead and write bi search with the size of 18 and it is a self-closing element and now we have to import this so go to the top right here and write import to open square brackets sorry curly brackets from react Dash icons slash bi like this and if you save you can see that we have a nice little element here with our filters so we have anywhere any week and add yes once we click on that it's actually going to open a model but we're going to do that later and also depending on the filters we select it's going to change these labels so if we select for example Croatia is going to change anywhere to Croatia and if we select a duration of five days it's going to write five days and same with the number of guests great now let's go back into our navbar component and let's add the last element which is user menu so go ahead and write user menu like this again we're gonna get an error but don't worry just go into navbar and create a new file user menu That's DSX first things first let's name it use client again and let's just write user menu as the name of the component and it's going to return a div go back into navbar and just import this at the top right here as same as we did with container logo search right and now you can see that our element has been centered but we're going to write some more things in this user menu now all right so go ahead and write the following class name for the first div which is relative now inside of that create another div we're going to have the following class names Lex Flex Dash row item just Center and GAP Dash three and inside of that let's just indent this div because it's an inner div this div is actually going to have an on click function but for now we're just going to write on click to an empty function and we're going to replace that later when we add some models which can open but what we can do is give it some Styles so let's go ahead and let's write hidden and the block tax Dash xn font semi bold py-3px-4 rounded Dash 4 however PG Dash neutral Dash 100 transition and cursor Dash pointer great and just write the text Airbnb your home that is going to be our button uh when when which when we click is going to open the rent model of course there is no functionality for that now but we are going to add it when we add some models and next to that we're going to have a user menu so just below this ad Airbnb or home div create another div and it's also going to have an on click function which for now it's just going to be an empty function as well and let's give it some classmates so class name b-4 MD py-1 and the the X-2 border Dash square brackets 1 pixel border Dash neutral 200 Plex Flex Dash row items Dash Center three rounded Dash full cursor Dash pointer hover Shadow MD and transition great and you can probably see but we have a very small button right here and now we're going to add some elements inside which are going to make it look better so first I'm going to add our menu icon so for that we're going to write AI outline menu and we can import that from react icons so let's go to the top right here and let's write import a outline menu from react Dash icon slash AI great and if I save you can see that we have a little hamburger right here great and just below that we're gonna write another div which is gonna have a very simple class class name hidden and the block like this and inside of that we're actually going to create another element called Avatar if we save of course we get an error so let's go into the uh not into nav bar but just in the components because we are going to reuse Avatar uh one more time in our project and let's create a component in the components folder avatar.psx of course that is also going to be a client function let's name it Avatar like this and what it's going to return uh is actually an image so we're gonna write image which you can import from next slash image it's a self-closing tag and we're going to give it a class name of rounded Dash ooh I of 30 a bit of 30 as well out of Avatar and source which is going to be for now just slash Images slash placeholder that jpeg and now let's go back into user menu and let's import this Avatar right here at the top so import editor from dot dot slash Avatar why can we do this well because user menu is in the navbar folder and Avatar is outside great and now we have a broken image right here and that is because we have not added the placeholder in our images so go back into my repository in the link is in the description and go back into images where you downloaded the logo and now go into placeholder.jpg and just save the file again drag and drop it into your images folder and make sure it's named placeholder.jpg great now you can head into Avatar and you actually don't have to do anything you can just refresh your page and as you can see we now have a nice little menu right here and you can try and collapse this and as you can see it is completely responsive on all devices beautiful all right and now I want to add a little menu that is going to show here once we click on this so let's head back into our user menu right here and let's go to the top and let's write some states so const is open set is open is equal to use state from react and default value is going to be false so you can import your state from react right here great and I want to write another function const toggle open which is a use callback function which you can also import from react and all it's going to do is it's going to reverse the current value of is open so set is open take the current value and just return the opposite of that value using the exclamation point like that great and we can add this to our second on click right after this Airbnb home we can add toggle open like that perfect and now I want you to go all the way here to the bottom and write a conditional it's open using the new state that we just created and if it's open we're going to render another div right here with the following class names the class names are going to be absolute rounded XL Shadow MD W square brackets 40 VW MD W Dash three quarters BG is going to be white overflow it's going to be hidden right position is going to be zero and top is going to be 12 and X is going to be small great and now inside of that let's create another div with the class name Flex Flex Dash call cursor Dash pointer like that and now if you click you're actually not going to see anything because we have not added any elements here yet so in order to do that we're gonna have to create another component which is going to be called a menu item so just open fragments like this inside because we're going to need need it for something later and you can just write menu item like this if we save we're going to get an error so go back into components navbar and create a new file menu item.vsx and let's just return a dip for now and let's go back into our user menu and let's import menu item from dot slash menu item right here at the top perfect and now let's make sure that this menu item is also a client component so use client like this at the top and let's add it an interface for this menu item so interface menu item props is going to accept an on click function which is an empty void and it's also going to accept a label which is a string now let's assign these props so react functional components and menu item procs like this and we can extract them in this function so on click and label perfect now let's style this div so class name px-4 py-3 hover BG Dash neutral Dash 100 transition and font Dash semi bold and inside the tip we're gonna use these curly brackets to write label which we have right here and we're also going to add an on click function right here which is going to use this on click prop right here great now let's go back into user menu and let's add some properties here so for on click you can just write an empty function for now but for label you can say login like that and once we click here we can see that I have my little login option and you can copy and paste this below and just write sign up for the second one great uh what these buttons are going to do they are going to trigger models which is something we are going to do in the next part of this tutorial so before we wrap this tutorial up I want to see where we are with our structure so in our app folder we have the new components folder right here we have two components Avatar and container inside the very components folder but inside of navbar we have logo menu item navbar search and user menu now let's just go through each of these components and let's see if they have the use client which we need at the top so logo has it menu item has it navbar does not have it so let's go ahead and add use client to the nut bar like that search has use client user menu has used client Avatar has used client and container has use client great job you just finished the UI for our navigation and you can try and play around and see how it works on mobile great good job so in this part of the tutorial we're going to create our register model but before we go there I want to show you a little bug that we have in our application it's kind of hard to reproduce but I figured away if I refresh and start clicking on things you can see that we have this unhandled runtime error where text content does not match server rendered HTML this is also called a hydration error it is something that I'm not 100 sure if it's our mistake or a mistake in the experimental app folder I saw a couple of issues on GitHub regarding this and it's not 100 been resolved whether this is a developer mistake or something with nex13 but these kind of things are to be expected because app folder is still in its experimental phase but don't worry I found a way to fix this issue so we will not be getting these errors either in development or production so let's go ahead and let's close everything up if you really want to reproduce it try and refresh many times and while it's loading try and click anywhere on the page basically the interaction that we are doing triggers an error for some reason so in order to fix this let's just refresh one more time so everything is fine and don't click anywhere so make sure everything is clear and what I want you to do is I want you to go into app into components right here and create a new file called clientonly.tsx self client only is going to be inside the root components not inside navbar inside components right here let's name it the client only like that and it's actually going to do it's going to check whether we are in server side rendering or not so for that we're going to have to use state so const has mounted set has mounted is going to be equal to use State and by default it's going to be false you can import use state from react right here at the top and since we are using use State this means that this component needs to be a client component so make sure you define use client in the first line right here all right and now we're going to use use effect as well so the moment this component loads that means that it has finished with the server side rendering and it can be mounted so let's just say set has mounted to True right here and we're going to add a conditional here if it hasn't mounted so if my bad has mounted like this return no so if our application has not mounted we're going to return now otherwise you can open brackets like this just write a fragment and just write children like this we're gonna get an error because we have not defined children so let's go ahead and let's do that in this props we're going to write children here and for that we also need to write our interface so interface client only props it's gonna accept children which is a type of react that react node and just assign these values here so react exceed is equal to client only props great so now we have this component which can serve as a wrapper around every component that we want to protect from this error of hydration so what I want you to do is I want you to go into layout.tsx right here and I want you to wrap navbar with our client only component so go ahead and write client only which you can import from dot slash components slash client only like this and wrap our nav bar so yes wrap our navbar inside client only and you can save the file and now if you refresh you can see it for a brief second our navbar does not exist and that actually fixes the issue we have you can see no matter how many times I refresh or click on the page I can no longer get the hydration error so we just fixed this for both development and production and we're gonna use this client only to wrap any of our client components which might cause problems with hydration great and one of those components is going to be our model component so let's go ahead and let's actually add this model component inside this client only right here let's save and let's solve this model is not defined error right here so for that we're going to go into components and we're going to create a new folder called models inside these models create a new file called model.dsx right here name it model like this and just return an empty div for now and now go back into your layout.tsx and just import model from dot components slash models slash model so our structure now looks like components we have models where we store our model navbar where we have our navbar and Avatar client only and container which are in the root of components right here great and now what I want you to do is I want you to create an interface for this small topic so let's go ahead and write interface model props which has the following properties is open which is an optional Boolean on close it is avoid on submit which is also a void idle which is an optional string body which is an optional react react element Twitter which is also react.react element like that action label which is a required string disabled it is an optional Boolean secondary action which is an optional void and secondary label which is an optional string so let's go ahead and let's assign these props right here so react functional components is going to say use this model props right here and let's extract all of those props here so it's open on close on submit title body Hooter action will disabled and secondary action and secondary label like that perfect and now let's write some callbacks some use effects and some states which are going to be needed for our model so let's go ahead and write show model and set the show model which is going to be controlled by use state which you can import from react and the default value is going to be false so you state has been imported by react right here and what does that mean if we use U state in our app components that means we have to Define it as use client at the top so very important make sure you define this as use client all right uh below this let's go ahead and let's write a use effect function so use effect which you can also import from react right here at the top it's going to do the following function make sure you add an empty dependency array here and just write set show model and we are going to assign this is open prop right here and since we added this is open prop right here we have to put it in this um uh dependency area right here and we have a small error here in order to fix that we are also going to use is open in the default use State value right here so make sure that is open is the default value of use state which controls the show model and set show model right here Perfect all right now let's write our handle close function so go ahead and write const handle close is equal to use callback and inside first things first we're going to check whether the model is disabled so if the model is disabled we're just going to break the function and not allow anything to happen once we click on the close button otherwise we're going to turn our local Sideshow model to false so set show model is going to be false like this and then we're going to open a timeout set timeout which is going to be last for 300 milliseconds and then it's gonna call on close like this so let's fill our dependency Ray we need disabled and we need on close like that so why are we doing this set imod why not just call the on close which is uh also going to change the show model at one point well because we are going to add some animations which are going to take exactly 300 milliseconds to to do so that's why we want to delay the on close so it doesn't break our nice animation of opening and closing the model so that is the only reason why we are doing this now let's write our submit function so const handle submit use callback and what this callback is going to do is also check if the model is disabled in that case it's going to break the function otherwise we're going to call on Summit like that and just make sure you fill the dependency array with disabled and on submit Perfect all right and now let's write some uh functions for our secondary action which will most likely be a previous and the next button in our model so const handle secondary action is also use callback foreign and we're going to check whether the function is disabled or if we don't have a secondary action at all so if disabled or if we don't have a secondary action at all in that case we can just break the function no point in doing anything forward but if it's not disabled and if it has a secondary action we can just execute the secondary action right here and make sure you add disabled and secondary action to your list of dependency arrays great and now we're going to do conditional here so if is not open meaning exclamation point is open we're going to return the null like that perfect and for that to work we now have to go back into layout.tsx and for now this is just temporarily here we will replace this with actual login models and rent models in search models but for now I want you to see how and why we are styling this model component so just to make sure it's opened just put is open like this this defaults to true so writing is open like this is the same thing as writing this so no point in doing that you can just write is open like that and now go back into your components models model that TSX right here and let's get into styling this component so I'm going to replace these dips with an empty fragment right here because not all of them are going to be under the same apparent so open a div right here and let's write some of the first class names here so class name is going to have justify Dash Center item slash Center Flex overflow Dash x dash hidden overflow open my back flow Dash Y dash Auto fixed inset-0 sorry incept Dash 0 like that Z dash 50 outline Dash none Focus outline Dash none as well and BG Dash neutral Dash 800-70 so slash 70 will reduce the opacity of this background to 70 percent perfect uh and and of course I made a mistake it's not natural it is neutral so make sure it's neutral and now that I've saved this you can see how our background has faded a bit you can change this to Red for example and you can see the same effect but we want to use neutral for our case because it just looks better all right now inside of that let's write another div which is going to have the following class names relative W Dash u m d w Dash or slash six LG w-3 slash six Excel W Dash two slash fifths sorry two-fifths my six NX Auto H dash pool LDH Auto and mdh Alto as well so what we Define here are different uh width settings for our model regarding on whether we're watching on a medium screen large screen or extra large screen or if we are on mobile we just want to fill the entire screen Perfect all right now let's go ahead and let's enter this element inside and I'm gonna give it a name here I'm gonna give it a comment so it's easier for us to navigate so the element which we are going to write inside of here is named content so that's what I'm going to write a comment for you don't have to do this but I think it's quite useful to navigate now let's open a div here our content div and let's write a class name which is going to be conditional so we're going to open this curly brackets and we're gonna write template little strings inside and first things first we're going to write translate we're going to write duration 300 you remember this number 300 well that's by how much we delay closing our model because we want this animation to play that's why we say duration 300 here and h-4 and now we're gonna add this conditional show model so if it's open Translate Dash Y is going to be zero but if it's closed it's going to translate Dash y full like this all right and you can copy and paste this below and we're gonna do the same thing for opacity so if it's open the opacity is going to be 100 so the model is visible but if it's not opacity is going to go down to zero great and now let's enter this content right here and let's write another div inside and let's give it some class names so translate H dash full LGH Auto mdh Alto boulder dash zero rounded LG Shadow Dash LG relative Flex Flex slash call W Dash full BG Dash white outline Dash none Focus outline Dash none as well all right and inside of that we're gonna create our first visible element here so don't worry we're soon gonna start seeing things here and I want to give a comment here as well this is going to be called a header so let's open and let's write our header component here so write a div right here and give it the following class names blacks items Dash Center up scrolled a bit too far my bad so items Dash Center p dash six rounded Dash D so only rounded on the top justify Dash Center relative Warrior Dash b square brackets one pixel like that and you can see we have a nice little header here great job and now inside of that I'm gonna add a close button so I'm going to use the button element for that because it's the semantic proper thing to do and I'm going to give it some class names here so class name it's going to be p-1 border Dash zero hover opacity Dash 70 transition absolute and left-9 great and inside of that I'm gonna use an icon IO MD close and I'm going to give it a size of 18 and we have to import this iom the close at the top of our component right here so import IO and we close from react Dash icon slash i o like that and now we can come back and you can save and you see we have a little close icon right here now of course we have to give it an on close function so on close it's going to call our handle close method we have uh sorry not on close my bad on click so our button is going to have an on click function which is going to call the handle close which we have defined right here perfect great so we have our close uh button and now below that let's add another div right here which is gonna have the following class name text Dash LG font semi bulb and inside for now you can just write something to see so login model for example and you can see how that is going to look like but we are not going to hard code it we are going to use our prop title here so if I say nothing is going to appear because of course the title is empty if you want to you can go into your layout DSX and add a title right here which is going to say hello world like that and then that is going to appear here great let's continue developing uh our title right here so just below this div right here we're gonna write uh our body so I'm going to add a comment for our body right here and all I'm gonna do here is I'm going to use the body element which we will pass from other uh components and let's give it a class name of relative e-6 and flex Dash Auto great and now you can see we have space for our body element right here all right and now below that I'm gonna create our footer which is going to be uh pretty similar but it's also going to have some actions inside so the div the class name for our footer is going to be uh Plex Flex call gap-2 mp-6 like that all right and inside we're gonna have an inner div which is going to have a class name of flex Flex Dash row iPhone Center and gap-4 and W Dash full like that great and first thing I want to do is I want to add an empty button here for now and of course we're going to get an error because we do not have this element so when I save we're going to get an error because button does not exist all right so let's go and let's go into our components let's close everything and in our components create a new file called button.dsx great and let's just name it button and all it's going to do now is return an empty button function for now so I can go back into my model right here and I can import this button from button right here at the top so import button from dot dot slash button like that and if you save the error is gone great job all right so we're going to continue to work in our button now so make sure you go into components button right here and let's create an interface for this button first so interface button props it's going to take a label which is the string it's going to take on click element which accepts an event which is a type of react that mouse event which in open pointing brackets inside is going to accept an HTML button element like that and it's going to return avoid uh it's going to have a disabled property which is an optional Boolean it's going to have an outline property which is also an optional Boolean it's going to have a small property which is also an optional Boolean and lastly it's going to have an icon which is an optional type of Icon type which you can import from react Dash icons because we're going to use that to add an optional icon to this button whether we need it so let's assign these procs here button rocks like that and let's extract all of these values label on click disable outline small icon like that great and now let's style our button right here so I'm gonna expand this and first thing I'm gonna add is a label inside of this button and to see what we're doing let's go into our model right here and let's write label uh for now we can say my button like this all right and you can see we have a button which is not styled at the moment but we're gonna fix that so go back back into button.phsx and let's give it some styles so class name it's actually going to be a conditional class name so open template literal strings like this inside curly brackets and let's write some static Styles first so relative disabled opacity-70 disabled cursor Dash not Dash allowed rounded LG over opacity Dash 80 transition and W Dash cool you can see some changes have started to appear on our button but still nothing too visible yet but that's gonna change now so write a conditional outline so if we select an outline prop for our button we're gonna give it a style of BG white otherwise by default it's going to be BG Dash rows Dash 500 great so we have added color to our button clicking copy and paste this two more times so for the second one we're not going to change the background we're going to change the border to Black otherwise we're going to use border Dash rows 500 so both background and Border are the same for the default button but core outline background is going to be white and border is going to be black all right and for the third option right here we're going to change the text to Black but if we are using the default button text is going to be white so it contrasts uh the red color nicely great all right and now let's uh do some conditionals for the small option so if you select the small option we're going to use py-1 but on a regular button it's going to be py-3 like that you can see how it expanded now we're also going to change the text size for the small option so text.s and on small and text MD on large ones I can copy and paste the small again and at this time we're going to change font light if the option of the button is small otherwise font Dash semi-evolved like that and last thing we're going to change is the border so on small buttons border is going to have a one pixel width and on large ones what is going to be 2 like that so you can see we have my button right here and you can go back into model.tsx and you can play around and you can write outline for example and you can see how that makes it the secondary button or you can write small you can see how that makes it a small button or you can mix and match outline and small to see how that would look great but for now you can just leave it default like this and now I just want to add the option to add an icon here so just here you can add a conditional icon now where do we get this icon for well we have to create an alias from this prop so just write icon like this and then we're going to use this icon option here so icon and put the conditional right here and we're just going to render the very same icon which is a self-closing tag it's going to have a size of 24 and a class name is going to be absolute left-4 top Dash three and we're gonna see this when we use uh Google uh icon for example if you really want to see how this look you can add an iPhone here for example let's say we we use iom the close here so I'm going to add this here for example and you can see how it added the icon on the left side here but this is just an example of course uh great and that finishes our button one thing we have to do for our button is add the on click and disabled elements here so make sure you add on click on click and disabled disabled like this and also make sure you turn this into a client component so use client at the top very important we don't want to import server components into our client components great so that's it for the button one more time confirm that you have added eu's client and confirmed that you used all of the props that we passed into the button good job now let's go back into our model and let's uh actually uh create a proper way to display these buttons right here so this is going to be our primary action button and it's going to accept the following props it's gonna accept disabled which is going to be controlled on the general disabled prop uh we're going to have a label which is going to be from action label and we're gonna have on click which is going to trigger handle submit function great so now because we have no action label nothing is displayed in this model but again you can try it out in layout that TSX you can add action label for example and say submit and you can see how it's passed through the model to our button right here great so let's go back into our model.tsx right here and we're going to add another button right above this button but this one is going to be a bit different because it's gonna have some different options so disabled is still going to follow disabled a label is gonna follow secondary action label uh I'm gonna rename this so we named it secondary label I want to name it the secondary action label so secondary action label like that make sure it's named secondary action label it doesn't really matter I just want to be consistent if you want to use secondary label you can do that as well but I'm going to use a secondary action label to follow this action label principle right here so make sure you just use a secondary action label here as well great now it's throwing us an error because there is a possibility that secondary action label is uh not available but before we fix that let's just change the on click of this upper button to handle secondary action like that and now we're going to conditionally render it so we're only going to render it if secondary but sorry before we do that we also have to add outline to this button as well because it's going to be secondary all right and now we can write a conditional which is going to render this button and that conditional is the secondary action and secondary action label and and now we can open the parenthesis and we can wrap this button inside and of course close the conditional like that great uh so what we're gonna do now is we're gonna create our login model which is gonna use both of these buttons and all of these options right here great so let's go ahead and let's close everything up for now uh sorry I said login model we're going to create our register model first so before we do that I want to create a hooks folder right here and inside of that we're going to create use register model.ts and for that we're going to need another package so go ahead and shut down your application and write npm install to stand great and make sure you run npm run web again and also make sure you refresh your application immediately after you run npm run Dev and make sure it compiles and loads because there is a chance that if you don't refresh it's not going to show you the latest updates great so it's refreshed and I can safely close my terminal now so in this use register model hook right here which we created in app hooks folder here you can import this shot create like this from to stand and now we're going to create our store interface so interface register model store is going to have the option is open which is a Boolean it's going to have the option uh on open uh which is a void and the same function is going to be on closed like that great and now let's just write const use register model is equal to create like this which is going to use the register model store so we give it a typo to this create function here and just open the function open another function and extract this set property open parenthesis and open an object inside the object is going to have is open to pulse on open it's going to be a function which is going to use the set property to set a different object which is going to be is open to true and you can already guess what on close is going to do so on close is going to be the same thing as an open accept is open is going to be false like that perfect and make sure you export the default use register model like that great and now we can go ahead and go into our uh components models and create a new file register model.vsx register model like this and let's just return an empty div and before we continue let's close everything up and let's go into app layout.dsx and let's remove this model from here and let's use our register model like this and make sure it is inside this client only wrapper because model is value models are very interactive and they will definitely break the hydration and you can remove the model import which we only used to see how we are styling our model so now it's going to go away but don't worry we're gonna fix that in just a moment so let's go back into our register model so go into components models register model right here and let's add some imports so first use spline right here and now we're going to need axis so import axis from axios which we don't have at the moment so I'm gonna shut down my application and I'm going to run npm install axios don't close the terminal yet because we are going to need some more packages here so after that let's import AI if you feel GitHub from we have Dash icons slash AI like this and now let's after that let's import FC 30 F lowercase C Google ROM react Dash item slash FC and let's import use callback and use state from react like that and let's import build values submit Handler and use form from react Dash hook dash one so that's going to be another package that we are going to need to use and just below that let's import use register model from App slash hooks use register model now let's go ahead and let's run npm install react Dash hook Dash form great and now go ahead uh let's just check if the types are okay okay so everything is okay and you can just go ahead and run npm run Dev again and again make sure you refresh your page great after the page has refreshed you can just close the terminal and let's get to actually using all of these Imports right here so I imported use register model from add sign slash app slash hooks use register model if you don't if you want to you can also use the dot dot uh abbreviation of that it's like this but I'm gonna stay with my ad sign right here all right so first things first we're gonna add this register model so register model is going to be equal to use register model like this we're gonna get our controls from that now let's add our login States const is loading set is loading use state by default is going to be false all right and now I'm going to write our use form here so cons we're going to have register we're gonna have handle submit we're gonna have one state from which we are going to extract errors like that which is all going to be equal from use form which is in pointy brackets going to accept this field values type that we have imported from react hook form like that and it's going to be a function which is going to have an object of default values in which we're going to set our default values and those are name email and password so we have established our form control with this function great now I'm going to write our uh on submit function cell const on submit is going to be a type of submit Handler which we have also imported from react hook form right here so on submit is a type of submit Handler which is also going to work with field values the same way this use form is working with them and it's going to accept some data so data and just open a function like this and what we're going to do here is we're going to set set is loading true like that and then we're gonna initiate an axios postcode to the register endpoint which we're going to create in the next part of this tutorial so for now we're only going to write in the UI but we can still write this axis post so axis that post is going to go to slash API slash register and it's going to send data great we can just safely pass this data because we know that data is filled values which is name email and password so this saves us a lot of time and before we write a semicolon here we're going to use the dot then option here we're going to open a function and what we're going to do for now is just register model dot on close so we're going to close this register model which we're working with if we have successfully registered great then we're going to write dot catch error and for now we're going to console log error like that and finally we're going to turn off loading so set is loading is going to be paused like that great and that's all we need for on submit for now but before we execute it we will actually need to wrap it in this handle submit from use form but we're gonna get to that part uh all right and now let's go ahead uh and let's actually uh change this div to a model state so go ahead and write model like this which you can import from dot slash model because they are in the same folder right here all right it is a self-closing tag and we're going to add some of the fields right here so it's going to be disabled if it's loading so if we are submitting and that means that we are loading something so we don't want the user to be able to change anything in this form all right is open is going to be triggered from register model that is open why does this register model have is open well register model is this hook use register model which we have defined right here and it has options is open and on open and unclose options great so next thing is going to have it's going to have a title which is register is going to have an action label which is continue it's going to have an on close which is register model that on close like that with the lowercase l is going to have an on submit function which is handle subnet open parenthesis and inside right on submit so that's what I was talking about when I said that we're going to need to use this handle submit um to wrap this on submit function right here all right and it can actually save this now now what is the problem why are we not seeing anything well that is because if you go into uh hooks use register model is open is false by default but if you change it to true you can see that we have our register model right here but we don't have to change this like that so just revert this to false and let's actually add this on open function to our user menu once we click on sign up so in order to do that let's go into let's go and close everything let's go into app components enough bar and let's go into user menu right here and let's scroll all the way down until we see this sign up right here and this is where we're going to add our function so go to the top right here and just write const register model is equal to use register model like that from slash app hooks use register model and now we're going to use this register model in this sign up right here register model dot on open like that and now if you go and click on sign up you can see how nicely our model uh we see you can see how what a nice navigation we've made because of that duration that I was talking about on open and both on close a very nice animation so keep it open for now because we're gonna add some more stuff in this model so let's go ahead and let's close everything up and let's go into app let's go into components models register model right here if you remember our model accepts two more fields and that is body and footer so let's write our body content first so for that let's create a constant body content like this and we're gonna write a div we're just going to have a class name of flex Flex slash call and GAP Dash four and we're gonna pass this body content sorry buddy and we're gonna pass the body content variable like this and if you actually say hello model buddy you can see that we have accessed our model body great job okay so the first component we're going to write here is the heading component which is something we're gonna have to create so go ahead and write heading like this and if you save we're gonna get an error so let's go into our components and let's create a new file heading that's the SX let's name it heading and we're just going to return a div and I want you to go back into models register model and just import this heading so we fix this ugly looking error so you can import heading from dot of Slash heading here in register model create no error and just make sure you open your register model right here so you can go into the user menu and click sign up like that and let's go and write some code in this newly created heading component so before we start let's mark it as use client because because it's going to be used in client components and let's create an interface for it so interface heading crops is going to accept a title which is a string subtitle which is an optional string Center which is an optional Boolean like that and let's assign this props right here having a props like that and let's extract all of these values so title subtitle and Center like that great and now let's add some class names to this so last name is going to be a conditional so if we have a Boolean Center in that case we're going to use text Dash Center otherwise we're going to use text Dash start like that and inside open another div and give it a class name of text to excel font Dash bold and give it a value of title like this and below that another div with the class name on dash light backslash neutral Dash 500 and mt-2 and give it a value of subtitle great you can now close heading and you can go back into components models register model right here and let's give this heading some inputs so title is going to be welcome to Airbnb and subtitle is going to be create an account great so we have our title and we have our subtitle you can also try and Center it for example you can see how that looks I prefer on this side right here great and now we're going to write our inputs where we will able to uh where we will be able to put our email name and password and other all the other fields great so create an input right here and you already know the drill we're gonna get an error but for that all you have to do is going to create another folder uh inside components folder called inputs so new folder inputs and inside of that new file input.psx great let's name it input and all we're going to return for now is a div and let's just mark it as use client so we don't forget that all right and now let's go back into our models register model and let's import this newly created input you can import it from uh dot dot slash inputs slash input like that and once you save the error should go away just make sure you open the register model again great and now let's add some props to this input so interface input props is going to be equal to IB which is a type of string label which is a type of string type which is an optional string disable it is an optional string as well pardon it's an optional Boolean format price which is an optional Boolean as well which we're going to use when we are displaying prices in our inputs required which is an optional Boolean and we're going to have a field called register don't confuse this with the action of registering and creating a user no register is a field which we're going to need for react hook form so we're gonna need this in our forms and it's going to be a type of use form register which you can import from react hook form right here and you can open the pointy brackets and write field values inside which you can also import from a react hook form like that and it's going to be required and then you're going to have errors which is a type of field errors which you can also import from react hook form so I'm just going to collapse this so we can nicely see what we're working with great and now that we have this input procs let's assign them here so react.fc into the props and let's extract all of them here so we're going to have an ID we're going to have a label we're going to have a type which is a default of text we're gonna have the disabled option you're going to have format price we're going to have register we're going to have required we're gonna have errors like that great and now let's go ahead and let's style our components a bit so in this div I'm going to give it a class name of w Dash pool relative like that and first thing I'm going to do is I'm going to create a format price conditional so format price and end and all it's going to do is going to render an icon bi dollar you can import bi dollar from react Dash icons bi which I'm going to do now so import bi dollar from react Dash icon slash VI like that all right if we have a bi dollar we're gonna give it a size of 24 and we're going to give it a class name of text Dash neutral Dash 700 absolute position top Dash 5 and left Dash two all right and now let's actually write our input element here so you can hide this for now we're gonna of course fill all of that so ID is equal to ID disabled is equal to disabled and now we're gonna open curly brackets like this and we are going to spread register function which we have passed and we're going to pass the ID as the first parameter and then we're going to pass the required option inside of the second parameter which is an object great and now below that we're going to add a placeholder this is important it needs to be an empty space so not just this it needs to be an empty space the reason we are doing that is because we're going to do a trick uh where our label and input are going to have a nice floating animation so I just refreshed so make sure that you have this okay so we're getting an error for register let me just fix that real quick the error is coming is because we have not passed anything in this input right here so let's go ahead and do that because we actually can because we have the register field right here so you can go ahead and you can edit some values so let's give it an ID of email let's give it a label of email with a capital E disabled is going to be is loading register is going to be register errors is going to be errors and we're going to manually write required like that and if you refresh and going to sign up again the error is gone but we are still not seeing our input so for that let's go back into our inputs input right here and let's finally style this so after placeholder add a type which is type and now let's write the conditional class names so open class name open curly brackets write template literals here and let's write some static ones first so it's going to be a peer class so we can control our sibling you're going to see that in a second W Dash full b-4 pp-6 font dash light BG Dash White order Dash 2 rounded Dash MD outline Dash none transition disable opacity 70 disable cursor not allowed now we're going to add a conditional format price so if we have a formal price option we're going to add a padding last nine otherwise default is going to be heading left 4. now we're going to add an option if we have an error so errors ID like that so if in the errors object we can find the ID of this input in that case we're going to change the border to rows 500 which is an error color otherwise it's going to be border Dash neutral 300 like that and you can copy and paste this one more time and we're gonna change this one to also cover the focus as well so Focus border Rose 500 and otherwise it's going to be Focus border and it's not going to be neutral 300 it's going to be black like that so if there is an error it's going to be Focus border Rose 500 otherwise it's going to be Focus modern black and as you can see we have our input right here great job all right and now let's add a label here which is gonna nicely animate to the top you're gonna see how cool that effect looks like all right uh so just below this press enter and let's write label element inside you can just put label like this right now it's here at the bottom but we're gonna style that to make it look better so open class name conditional open template literal strings and inside of that you're going to write absolute tax Dash ND duration 150 milliseconds transformed minus Translate minus y minus 3 like that you can see it's moving all around but don't worry we're gonna fixate it top Dash five z-10 origin Dash square brackets zero we're going to add a conditional format price left-9 otherwise left 4 you can already see it starting to get in its position now I'm going to remind you we added this peer class to our input that means that we can control what happens to the label on the various actions that happen in the input field so that's what I'm going to do here I'm going to write peer Dash placeholder Dash shown hail Dash 100 so it's a long command but that's what we're going to do and then below that here Dash placeholder Dash shown again and I made a mistake so placeholder like that it's gonna have translate Dash Y dash zero like that so now you can see that when I click well nothing is yet happening but just wait a second here Dash Focus so when I focus on input we're going to scale it to 75 percent and when I focus on input it's also going to minus translate minus y minus 4. and now you can see what a cool effect this actually is when we click we can see how it hovers above our text great and we're also going to add an option for error so errors if we can find an error by this ID text is going to be rows 500 otherwise the text is going to be sync 100. great so you can see that we can now write and see how nice this looks great job uh you have successfully finished the entire input good job and now let's go back in the register model right here and let's add some other fields it's going to have an input field sorry an email field but it's going to have two more Fields here so the second one is going to be name so give it an ID of name and a label of name as well and last one is going to be password make sure you give it a type of password as well and the label is going to be password like that and you can actually try and click continue and you can see how our validation is working why is it working so easy like that well because on submit we wrapped our on submit function with this hook uh handle submit like that so great it's not going to allow us to submit anything if we have not field all of the fillets so I'm going to fill the fields now and what's happened well we probably got an error I mean we definitely got an error because this axios post does not exist so I'm gonna actually open my console a little bit and you can see when I click continue I'm getting an axis error because this does not exist yet so all I want to do now is I want to replace this console log with a toaster so in order to do that let's open our terminal one more time and let's shut down the app and write npm install react hot post great and now run npm run Dev again and make sure you refresh all right so I'm gonna open this up one more time we can close the terminal now and I want you to create a new folder inside this app folder called providers so providers like this and inside of that create a new file toaster provider dot TSX like that great well you want to mark it as use client and you want to import poster from react Dash code dash toast like that and all you want to do is return poster provider like this it's going to return a toaster which is a self-closing tag and expert default toaster provider so why am I doing this what the need for it why did I not just use this wherever I need to use it well this is the problem with well not a problem but we have to change our way of thinking toaster is a foreign Library which means it's not adjusted the next 13 app router here so it is a client this is a client component which we want to use inside our app so the way we do that not always but especially when we're using this npm packages like this we have to create a little provider or a wrapper around it so it needs to have at least one client parent right and now we can use this toaster provider in our layout.dsx right here so you can actually go into client only here and you can feel free to add toaster provider like this and you can import it from dot slash providers toaster provider you can try and use toaster like that but you're gonna get an error you might get an error actually yeah because we are importing a component that needs use effect and it requires at least one parent which is a client so make sure you import toaster provider which we just created that is the reason why we did that so just to wrap it up in our client only we now have hosted provider register model and navbar and now that we have the toaster provider here we can actually replace our failure message here so I'm gonna go back into my register model and I'm going to write instead of console log I'm gonna write post which I can import from react hot toast uh right here react hot toast toast that error and I'm just going to pass error like that so if I try now oh made a mistake so we cannot use this error I'm just gonna write something went wrong like that just refresh going to sign up let's try again and you can see something went wrong great and I'm just going to change the capitalization Perfect all right and now what I want to do is I want to add uh placeholders for our social signing so Google and GitHub right so let's go ahead and let's create just the way we did with this body content we're going to create footer content now so const footer content is gonna have a div and give it a class name of Plex Flex slash call yeah four and sorry gap-4 empty Dash three and let's just place it right here so footer footer content so we can see what we are doing so let's start by giving an HR element right here and now let's write button which it can be self-closing like this let's give it an option of outline oh my bad we have to import the button so make sure you import button from dot dot slash button in the register model give it an option of outline give it a label continue with Google give it an icon FC Google and we can import FC Google forever actually imported at the beginning so we have react icon slash FC for FC Google right here great and it's missing an on click function so let's just give it an empty function like that and the reason we are not seeing anything is because I have not added the footer in our model so let's go back into components models model right here you can see I have this footer that is not used anywhere so let's go down to the bottom right here and just below this div which wraps our buttons let's add a footer like this and now if you save you can see that we have this continue with Google which we added in our register model so just to repeat your model that TSX needs to use this footer prop which is just below this div which handles the two buttons like that I forgot to put it so just make sure that none of the props you passed is unused here all right now we can go back into register model and let's continue working on the GitHub icon as well so you can just copy and paste this right here and this one is going to be continue with git shot and it's going to use AI build hit sub option great so now we have those two options and one last thing that I want to do is add another div it's going to have a class name of text Dash neutral Dash 500 tax Dash is Center mt-4 and font light and inside of that we're gonna create uh a div which is going to ask already have an account make sure you wrap that in a div as well like that and just copy and paste and the second is going to say log in like that and let's just give it a class name of flex Black slash row items touch Center and GAP Dash 2 like that and you can see we now have this uh question right here and I want to give this login a class name so it looks a bit different paper so text Dash neutral Dash 800 cursor Dash pointer and hover underlined and let's also give this text Dash Center option as well uh let's actually try with justify Center great so I'm just going to collapse this so you can see what I'm writing great so the main div is going to have justify Dash Center Flex flat throw item Center Gap to great and all this is going to do for now is going to close the model so we're going to use register model on close like that so if we click on login because we don't have the uh model yet it's just going to close it great good job you finish the UI for register in the next part we are actually going to create our Prisma our models our mongodb database and we're gonna make this completely functional because we're also going to create this API slash register route good job so in this part of the tutorial we're going to add functionality to our register model and we will enable creation of the user in order to do that we need to set up our Prisma and mongodb so let's head into our terminal right here and I'm going to shut down the application for now and I'm going to run the following command npm install Dash D Prisma after Prisma has been installed we can run the following command MPX Prisma init and press enter MTX Prisma in it has created a couple of files in our repository so let's take a look at them I'm going to collapse everything here and we have a new folder called Prisma right here and inside of that we have schema.risma it is set up to use postgra SQL we're going to change that and we also have another file right here dot environment which has been filled with the postgresql placeholder URL so we're going to change both of that because we're going to use mongodb so first things first let's go into Prisma folder schema.prisma and let's replace postgresql with mongodb like that now the second thing we have to do is we have to add the proper database URL that we can fill our environment file with in order to do that let's head into Google and let's search mongodb Atlas and let's select the first URL click on sign in and depending on whether you're logged in or not you might have to create an account I'm going to use Google login I already have an account for this great you should be greeted with a similar page like this and you should find the button which says build a database in your case it might be in the upper right corner in my case it's right here now just make sure you select the free option right here for me it's the last option here and it says free right here and free here on the bottom as well provider does not matter as well as the region and the name can stay the same cluster zero so just confirm that you're using the free database here and free right here and just click create great now you have to create your user I highly suggest you use something very very simple for this tutorial for example my username is going to be Antonio I'm going to zoom in a bit so you can see and my password is also going to be Antonio if you're creating a secure password just make sure it does not have any special characters in it especially spaces uh question marks and stuff like that because that can cause problems and after you selected your username and your password just click create user great after your user has been created uh you should add your IP address to the list but in my experience a lot of people have problems with this because they have a dynamic IP address which means that sometimes it can happen that you cannot connect to your mongodb and you get a topology error in order to fix that I highly suggest you add the following IP address to your list of IP addresses 0.0.0.0 slash zero like this and just press add entry this is going to allow any IP address to connect to your cluster so just make sure you don't share the URL where it's not needed great and you can press finish and close and press go to databases right here great you should see your cluster right here and press on the connect button here and select Connect using vs code we're not actually going to be using vs code to connect but it gives us the type of URL that is compatible with Prisma so just copy this URL right here and now let's go back into our environment file here and I'm going to replace this database URL entirely and remove it I'm just going to leave the strings for now and I'm going to paste this new URL that we copied from here and what we have to do here is we have to replace this password with my actual password and if you remember a few moments ago I used a simple combination of Antonio and Antonio so I'm going to replace my password with Antonio like this and just make sure at the end you have a slash test or slash whatever your database Name Is by default it is going to be test so you can just leave it like this but important to notice if you don't have this at the end Prisma is not going to work great you successfully have the environment and now you can head back into schema.prisma right here and one thing I want you to notice if you're not seeing this nice syntax like I am for example my generator and client is blue color and I can clearly distinct what is a string what is a constant what is a function if you are seeing everything in a plain white text it means you're missing an extension so just go into your extension Marketplace and type in Prisma and you're gonna see the first right here I already have that so just make sure you install that so you have some nice syntax highlighting and formatting great now let's head back into our schema.prisma and let's create all of our models here so first model we're gonna have to create is the user model so go ahead and write model user let's give it an ID which is a type of string now because we are working with mongodb we have to map this ID to object ID which is a specific type that mongodb uses so go ahead and write the following at ID at default in parenthesis write Auto and execute the outer function and then you're going to write add map open parenthesis open annotations underscore ID and Lasting at db.object ID like that every model in our database is going to need to have this type of ID we're only going to write it once and we're going to copy it for the other ones great now our user also is going to need to have an email which is a type of string which is optional you can see that it's optional because we put a question mark at the end and also make sure that email is unique in order to make it unique you need to add an add sign unique like that great and just above email I want to add name which is also a string and optional like that great now let's add email verified that is going to be a date time and it's optional as well image is a string which is optional as well hashed password is something we're going to use for our credential authentication so using name email and password when we registered uh so that is going to be an optional string as well then we have created add which is a date time which uses at default and inside the brackets right now and execute it like that and then we have the updated app which is also a date time but they can use this special object updated at great and now we have favorites IDs which is a string of arrays sorry array of strings we and that type of string is DB object ID great so why are some of these fields optional or because we are also going to enable social login so we cannot set hash password to be required for example because hash password is not going to exist if we log in using Google or GitHub and favorite IDs to explain that a little bit so we're going to push array of IDs every time we favorite a listing we're going to push their ID here and that's how we are going to know whether we have favorited or haven't favored that a specific listing now we have to create a couple of relations to some other models which we will use and first is accounts so a user can have many accounts so for that you just have to write account and accounts right here we have not created the account model right here so we're gonna get an error but we're going to fix that in a moment another relation that user model is going to have is listings so go ahead and write listings and just write listing an array at the end and the same thing is going to be for reservations so user is going to be able to have many reservations great now let's go ahead and let's create the account model which is the first relation we have here this account model is going to be used for our social logins so it needs to have this exact Fields otherwise social login is not going to work so model account which you can just copy and paste the ID which we used in user right here and then you're gonna need user ID so user ID which is a string DB object ID side with the string provider which is a string provider account ID which is also a string refresh underscore token which is an optional string and a type of DB dot string access underscore token which is also an optional string VB dot string like that expires underscore at which is an optional integer token underscore type which is an optional string slope which is an optional string ID underscore token which is an optional string with DB that string at the end and session underscore state which is an optional string and now we just have to complete the relation between the user here so let's go ahead and write user in user at relation builds open an array and write user ID comma references ID and on delete Cascade like that so what this line does exactly is it creates a relation with the user using the field user ID right here and it references to the user model ID field right here so we're going to store this ID in this user ID field right here and on the lead we are giving it an instruction what happens to this record once user is deleted for example great uh before we move on I still need a couple of things in this account model so you're gonna write add at unique and inside of that you're going to write provider and provider account ID so we need a unique combination of these two Fields right here that is different than individually writing ad unique like this so using this unique we need a unique combination of the two great now let's create our listing model which is next so let's go ahead and write model listing and first things first let's just copy the ID either from account or user model great next thing the listing is going to have is the required title which is a type of string description which is also required in a type of string image source which is also required and a type of string which is going to aim at cloudinary CDN you're going to see that in the future then we need created at which is a date time let's use this at default and now inside of the parenthesis category which is a type of string room count which is a type of integer bathroom count which is a type of integer as well guest count which is a type of integer as well and location value which is a type of string now it's gonna have a user relations obviously we also need user ID which is a type of string but type of DB object ID as well and price which is a type of integer and just like we did with account we need to create a relation with the user one more time so let's go ahead and write user user add relation builds user ID references ID on the lead Cascade so exactly the same as we did with account again this user ID field right here will reference the user model ID field right here great and every listing is also going to have an array of reservations so let's go ahead and write reservations reservation like that and last field we have to create my apologies last Model we have to create is the reservation model so let's go ahead and write model reservation and inside of that let's give it an ID first which you can copy again either from listing account or user they're all exactly the same and next thing write user ID screen DB object ID listing ID string DB object ID also start date which is a date time end date which is a date time total price which is an integer and created at which is a date time that defaults now and now we have to finish these relations here so user user relation builds user ID references ID on delete Cascade but it's also going to need to have a listing relation so write listing listing at relation Fields listing ID references ID on the lead Cascade so you've already seen the user explanation but the listing explanation is exactly the same so we will be using the listing ID field in order to reference to an ID field in the listing model right here great and that's it for our Prisma schema what we can do next is we can actually push this to our database so right now if you click on browse collections in your mongodb you're not going to see anything right so we're going to run a command now which is going to enable us to push this to the database so let's go into our terminal and before you do this just make sure you added your database URL right here otherwise it won't be able to connect so let's go ahead and write MPX Prisma DB push and you can see that it's playing the following changes our user account listing and reservation collections which we have created great great and when you run pre npx Prisma DB push for the first time you will also be running this running generate function for me it took a bit longer around three minutes for you it might be instant depending on your network connectivity great and now if you go back to your cluster and click refresh you should be able to see all the new collections so we have account collection listing collection reservation and user collection great and now we have to install some packages which we will use for next out so I have the next out documentation here and these are the packages we are going to install so let's clear the terminal and let's write npm install next dash out at Prisma slash client and at next dash out slash Prisma Dash adapter so next out at Prisma slash client let me just expand this a little bit like this so these are the three packages uh which we are going to need next out prisoner client and next out slash Prisma adapter all right and just press enter to install those great and we're also going to need another package for our personal use for credential login and that is Big Crypt so go ahead and run npm install bigcrypt and since bigcrypt does not come with types by default you'll also need to install npm install Dash D add types slash vcrypt great and before we move on let's create our Prisma DB util cell in order to do that let's go and close everything go into app and create a new folder called Libs and inside of that create a new file Prisma db.ds like that and inside I'm going to import Prisma client from add slash Prisma client like that and first let's write some types for this Prisma so declare Global open curly brackets VAR Prisma Prisma client pipe undefined great and now just write const client is equal to Global this dot Prisma at at sorry pipe pipe new Prisma client and then just write if process.environment dot node environment is not equal to production sorry production like this Global this dot Prisma is equal to client and just export the default client at the end like this so we import Prisma client and we give a global definition of Prisma so it can work throughout our code then we create a constant called client which either searches for globaldists.prisma or it creates a new Prisma client and then we create an if clause which checks if we are in development by checking if we are not in production so if node environment is not in production then we set the Prisma client we set the globaldis.prisma to this newly created client right here so why did we write this code well because next js13 hot reloading can cause a bunch of this new Prisma client instances to be created giving us a warning in the terminal so this way we assign the Prisma client to a global this variable which is not affected by hot reload so this is just a best practice for using Prisma with next 13. if you want to you could technically import Prisma client everywhere in your code but I think this is a cleaner solution and it is actually recommended in the best practices for next 13. great um and next thing we have to do is we have to create our next out file right here now important thing I want to notice um as of what they date it is so as of uh 27th of March 2023 next out does not support the app API folder here so we cannot just create a next out file inside app folder this is the only instance where we still need to use the pages folder but don't worry the pages API folder will not be deprecated for a very long time because of the because of the migration period that next 13 needs to allow so let's go ahead and let's create a new folder and let's name it Pages this is already familiar to you if you're coming from next to 12. and inside of that create a new folder called API and inside of that create a new file open square brackets and write dot dot dot next out an outside squarebrackets.ds like this I actually made a mistake here and it should be inside out folder like on the picture but we're gonna fix that later but you can do it now if you want to great and and what I want to do now is I want to create uh our uh next out config great so let's go ahead and let's write uh export const out options which is going to have a type of out options which you can import from next out like this and let's just open this object right here and let's give it an adapter so adapter which we're going to use is a Prisma adapter which you can import from at next dash out slash Prisma adapter which is from one of the packages we installed before and this Prisma adapter needs to accept our Prisma client which we created right here so let's go ahead and import that import Prisma from Prisma VB like that and then you can pass in Prisma like that great now let's write our providers so providers we're gonna have a GitHub provider and you can import GitHub Provider from the following import GitHub Provider from next dash out slash providers slash GitHub like this and while we are here let's also import Google provider so Google Provider from next dash out slash providers slash Google like this great let's go back into git Club provider right here and you can open an object inside its function and it's going to accept a client ID which is a type of process that environment.github underscore ID which we're going to pass as string and below that it is going to accept client secret so client secret is going to be process.environment that GitHub underscore secret we're going to add this environment variables later great and that's it for GitHub provider and now let's copy and paste this and just replace this with Google provider and instead of git Club ID here we're going to use Google underscore client underscore ID and instead of client secret which is Google which is gitlab secret here we're going to use Google underscore client underscore secret like that great and now let's add our credentials provider so go ahead and write credentials which you can import from the same way we did with this so right then chose Provider from Nextel provider slash credentials like this open this functions object as well and let's give it some options so name is going to be credentials like this credentials are going to be email and password email is going to have a label of the email and a type text and it's also going to have a password label is going to be password and type is going to be password the next field in prudentials provider is going to be an async function authorize which is going to pass credentials to us like this and you can open the function like that and first let's check whether we have email and password passed so if there is no credentials question mark dot email or if there is no credentials question mark dot password in that case we can just throw new error invalid credentials so these credentials are going to be whatever the user's input is so if user forgets to enter password or email we'll throw an error that we are missing these credentials great now let's go ahead and let's find the user using our credentials email so we're going to write const user is equal to await Prisma dot user how does Prisma how to complete this user well because we run MPX Prisma DB push and we have defined our user in schema Prisma right here so that's the amazing thing about Prisma it offers us type safety in our models so you can write user dot find unique where email is equal to credentials.email like that great and now we're going to check whether this user actually exists so if we don't have the user or if the user does not have hashed password we're going to throw new error invalid credentials again so if the user that we found is invalid by not having a hashed password which means there is a mistake in how the user is trying to log in or if there is no user at all that means that this email that user entered does not exist in that case we throw an error and now let's check and compare if uh the password that user entered is actually correct for that we're going to write the variable const is correct password is equal to a weight bcrypt dot compare and the first argument in big crypto let's import B group before we continue so go ahead and write import bcrip from decrypt like this great if you're having an underline here saying that there are no types make sure you install npm install uh Dash capital D B Crypt types if you're seeing it like this that means everything is okay all right and in this bcpt compare we're going to pass credentials.password and we're going to compare it with user.hash password which is one of the fields that we have if you remember when we initialized our Prisma schema great and now let's check if the password is not correct so if exclamation point is correct password in that case we can throw another error again invalid credentials because the password that user entered does not match the hashed password which we have in the database great and in the end just return user so if it passed all of this validation then we are safe to give the user back to our client great now let's go to the end of this array of providers right here and let's write pages sign in and just write slash so whenever any error happens or if we use a weird types of callbacks for example it's going to redirect to our slash page which is our out page because we don't have a specific out page we use this main page with the navbar uh to see our login and register models right here all right and now right here I want you to write a debug process.environment.node environment is equal to development so you only want to enable debug if you are in development this is going to help you because you will see some errors in the terminal which you otherwise would not see for this session you're going to use strategy jvt like this and let's just pass in a secret process.environment dot next out underscore secret like this great and now let's just export default next out which you can import from next out slash next actually not from slash next so just from next out like this so import next out from next out and you can actually combine this without options right here so I'm gonna write next out comma out options and I will remove this option at the bottom so next out is imported from next out right here the same as out options great so just confirm next out and out options from next out right here and it's important that at the end you use export default next out and we need to pass the options so open uh open the function and write out options inside great and out options is this object we created right here and just save the file and now let's add some environment variables which we're going to need for now we don't have to add this we're going to create that in the next part but one we definitely need is next out underscore secret so go back into your dot environment file right here and just add next Out secret like this that is going to be our secret great and now let's actually go ahead and let's just keep our project running so we can catch any errors on time great you can refresh your localhost 3000 and just wait for it to compile great and now I'm going to close the terminal and what we're going to create now is this API route which we actually use in our register model so if you remember last time we pointed an axis at postcode to slash API slash register so let's see how we can create a post route in our new app API folder so first of all you can remove this hello folder right here we're not gonna need this hello folder and now let's create a new folder called register and inside of that let's create a new file called route.ds great and now let's write the following export asynchronous function post so this is amazing thing what we can do in the new route handlers we can finally Define custom post get patch Boot and the lid options you no longer have to use request method in a switch method great so our post is going to provide us with a request which is a type of request like this let's open this function and let's see how we can get our body so in order to get our request body we're actually going to use con's body is equal to a weight request.json like this and then we're going to extract all of the fields we need from our body and that is email name password from body like this and now let's hash our password so const password is equal to await requip let's import decrypt at the top so import decrypt from decrypt and this liquid function is going to accept the user enter password and encrypt it and just make sure you write bigcrypt dot hash like this so not just pick Crypt hash password is await bcrypt dot hash pass in the user password and some settings to encrypt it great and now let's create our user const user is await is equal to await Prisma and now let's import our Prisma so import Prisma from Prisma VB so let's write await prisma.user.create data and let's give it the data of email name and password my apologies hash password so we are not storing password in the database we are storing the hash password so user is going to be a new user with the data of email name and hash password great and now to return the response we have to use next response so just write return next response which you can import from next slash server so nextresponse.json user like this great and now our route should be ready make sure your project is running so I'm gonna expand this a little bit just refresh to make sure everything is running if you if it isn't just run npm run Dev and I'm going to open my network tab right here to see if maybe we have an error and let's click sign up now I'm going to create an email with Antonio mail.com my name is going to be Antonio and my password is going to be one two three three two one I'm gonna click continue and you can see it pointed at our register right here and just like that we get a 200 success and let's see if we can actually see a preview and we can we have created our user amazing job and let's head back into our database here and let's quick refresh and if you load the users you can see we have successfully created an Antonia user with Antonio email and hashed password and default set created at an updated Fields great amazing job in the next part of the tutorial we're gonna finish the login and we're also going to enable social sign in so in this part of the tutorial we're going to enable our login model which we didn't have until now so in order to do that first let's create a hook which will control whether our model is opened or closed so let's go into app books and let's copy this use register model right here and let's rename it to use login model and let's also rename the store and the function so I'm just going to use this to rename it to login model like this so we have login model store use login model and use login model everything else is exactly the same so we controller is open and we have two functions on open and on close which switch is open to either true or false depending on wall function we call make sure you save this file great and now I'm going to close everything here and I'm going to go into components models and I'm going to copy register model and I'm going to rename it login model and we're gonna have to do some changes it's not exactly the same so let's start with the simple things let's rename the component into login model like this and let's make sure we export default login model like that great and now what we need to do is we have to find a way to enable this model so to do that first I'm going to close everything and I'm gonna go into app layout.tsx right here and exactly where we added the register model let's just copy and paste and add login model which you can import from dot slash components models login model let's just put them together so they're right here great now let's go back into our login model and we have to change what controls whether this model is opened or closed so instead of register model right here don't delete this because we are going to need it to switch to register model but below that just add const a login model is equal to use login model which you can import from App slash slash app slash hooks use login model let's put that right here so they the controls are together and now we're going to use this login model all the way to the bottom where we have our model component and we're going to replace all of this so register model is open and register model on close it's actually going to be login model all right and we are still not seeing anything here so let's close everything up and let's go into app components navbar user menu right here and you can already guess so we have to do this same thing for the login menu item right here and for that we're going to need a login model so go to the top and just as you did with register model write login model is equal to use login model and it's going to be imported right here let's keep them together like that and now we're going to use this login model in this menu item on click so login model dot on open great and now if you actually try and click login you can see we have the exact same model open because remember we copy and pasted it but now we can keep it open and we can go back into components models register model and let's actually write some changes so first thing let's give it a different title it's not register it's login and I made a mistake I opened the register model my bad so make sure you go into login model right here they look exactly the same sorry for the confusion so just to confirm go into components models login model right here and replace the title of register with login and now you're gonna see the login right here great and now let's just modify our body content so find the body content right here and instead of Welcome to Airbnb we're gonna write welcome back and instead of create an account we're gonna write login to your account like that so we get a bit of a different message and next we don't need the name input anymore so let's go ahead and remove that great so now we just have email and password and let's go to the top right here and we also no longer need the name in our field values here I made a mistake and I removed the email instead of name but don't worry your code is still going to work even with that but it should look like default values on the picture you can fix that now or you can wait till we fix it together later so you can go ahead and remove that as well and now instead of calling axis that post to the register function we have to replace it by using the sign in option from next out so let's go here to the top and let's import I'm just gonna write here at the top so you can see better let's import sign in from next dash out slash react like this great and we're going to use this sign in option instead so let's find our on submit function right here great and I'm just going to remove all of this and we're going to write the following so we're going to use sign in and we're going to use credentials and we're going to pass dot dot data so we can count that this data is only going to have name and password which is exactly what we need in our sign-in how do we know that well if you go back into Pages next out right here you can see that we have email and password for our credentials provider great so we have email and password Here and Now Let's just add redirect false no need to redirect after that then let's write dot then call back and let's write set is loading the boss right here and then we're going to check if the Callback went all right so if all back question mark dot okay so if we have successfully logged in in that case we want gonna call our toast toast which you already have imported uh from react hot toast right here because we copied this whole component from react from a register model toast that success logged in like that and we're gonna do a router that refresh we don't have our router so you should get an error here so let's add the router right here at the top cons router is equal to use router from next slash navigation and make sure you write it like this so const router is equal use router and X execute that function and use router is imported from next slash navigation not the next slash router that is the old way of doing it in the new way we're using slash navigation like that and then we can use this router to trigger router that refresh which is going to um update all of our settings sorry all of our active values here you're gonna see why we need that once we add the current user which is actually a really cool thing I'm going to show you that uh okay and after that I want to close this model so login model dot on closed like that great and I'm gonna write if callback question mark error so if we have an error happening well I clicked something sorry so if callback error in that case we're going to write post that error pull back that error like that so we're actually gonna provide this uh with the proper error great now let's go ahead and let's test this I'm going to close this I'm going to open my network tab right here and if you remember last time I created Antonio mail.com with the password one two three three two one all right and we get an error so let's see what's going on all right so it's a very simple fix we put our out next out right here in the wrong directory so just to make everything easier let's close everything let's go into Pages API and I forgot we did not put this in API we put this in another folder called out so in API create a new folder called out like this and drag and drop this next out file into that folder and click move so you should have Pages slash API slash out and inside of that you should have the next out.ds file right here great now let's go ahead and let's try this again so I'm gonna go to my logo host 3000 here and I'm going to click login and let's click continue and just like that we get our success message logged in great but we currently don't know that we are logged in there is no way of seeing that right so I'm going to show you a really really cool way of how you can fetch the current user in next 13 using server components so let's go ahead and let's close everything up and let's go into app and let's go and create a new folder called actions and inside of that folder create a new file get current user.ds like that so inside of get current user action right here you're gonna write the following we're going to import get server session from next dash out slash next like this and then we're going to import our out options so import out options from add slash Pages slash API slash out and you can open these curly brackets and write a dot dot dot next out because that is the exact name of our route if you go into Pages its Pages slash API slash out square brackets maxed out so make sure you import it like this and how do we have these out options well just make sure that you separately export this out options constant and you export default the next out right here so that's why we needed to separate these out options here all right now let's continue writing our get current user here and we're also going to need Prisma so import Prisma from flash app slash Libs slash Prisma VD like that and now let's write a quick function to get our session so export async function yes session and just return await get server session and pass in the out options right here like that and now let's write our get current user function export default async function get current user open a try and catch block and let's handle the error so catch error which is a type of any just return null I don't want to throw any errors for this get current user because this is not an API call don't get this confused this is a direct communication with the database through our server component so I don't want to throw any errors to unnecessarily break it we're rather going to use conditionals to fix that great now let's initiate our sessions account session is equal to await get session what is this get session well make sure it's this function you created right here which uses get server session with the out options and this is how we get our session in our server components and now we're going to use that session to find the current user but first let's check if the session is correct so if there is no session question mark the app user question mark.email return null this session does not actually exist and now let's find the current user so cons current user is equal to await risma.user.find unique where email is equal to session.user.email as string like this now let's check if we don't have the current user so if there is no current user we're also going to return now and if all of that checks have passed you can just write return current user like this great and now where are we going to use this get current user function well this is really really cool let's close everything and let's go into Pages layout.vsx layout date.dsx is by default a server component so you can feel completely free to write you could have written this entire get you get user action inside of this function right here but just because of the nicer structure I separated them so what you're going to write here is const current user is equal to await get current user and you can import get current user from dot slash X and action slash get current user now of course if we are using a weight that means we have to transform our root layout function to be an asynchronous function like that great and now we're going to use this current user which we just that easily got in our server component and we're gonna pass it to the nav bar right here so go ahead and write current user basically of the current user like that and of course we don't have the type for that so let's go into our components let's go into navbar and let's go into navbar.tsx right here and let's create an interface so interface navbar props current user isn't optional because remember it can be null and you can actually use user from at Prisma slash client this was generated once you run once you have run npx Prisma DB push so you can actually use the very same user type that you defined in your Prisma schema which is really really cool and you also have to add a pipe now like this great and now let's assign this it's a react FC and let's extract it from here current user like that and let's actually write console log current user to see what's going on so I'm going to extend this right here I'm gonna refresh and you can see we have our logged in current user without an API call I think that is absolutely amazing so what this goes to show is you can just as easily as you've written this get current user you could have literally written cons current user is equal to await Prisma dot user.find right you can you could have written that here no problem but we are using uh this separate action because it's simply cleaner to do it that way but you could have written this entire thing inside of this render function of root layout I think that is absolutely amazing um and now what we're gonna do we're going to close everything and we're going to use this current user to display a logout function here so in order to do that we have to pass the current user to our user menu so let's go into app components navbar navbar right here you can remove the console log and we're going to use this current user to pass it the user menu like this so let's create an interface here my apologies user menu prox is the name of the interface current user which is a type of user from Prisma slash client or null and it's also optional and just assign this so react FC user menu props and extract current user like this great and now let's use this current user to display different menu items depending on whether we are logged in or not so go ahead and just above this fragment here you're gonna write a conditional if we have the current user we're going to write something else otherwise we can display these menu items that's why we needed the fragment because they're not under the same because they don't have a parent like this and let's just copy and paste this right here so it looks the same now but we are actually going to change this here so if we are logged in we're going to display something different so you can just replace this with an empty function for now and like replace this with an empty function as well if we are logged in and the first label is actually going to be my trips the second is going to be my favorites so you can already see what's going on if you're logged in we're gonna display completely different options we're gonna display my favorites my reservations being on display my properties we're gonna create URLs for that later we're gonna create uh we're gonna offer here Airbnb in my home for mobile users because this Airbnb your home button this disappears on mobile and we're gonna have an HR here and the last menu item is actually going to be sign out sorry log out like this and now if I try and click you can see that we have a completely different view we have my trips favorites reservations properties Airbnb my home and log out and we no longer have login and sign up options here so let's enable this log out because it's the only thing we can do for now in order to do that it's very simple we're going to use sign out function from next out slash react so make sure that you have the import sign out from next dash out slash react right here and you execute it like this so open an arrow function and execute the sign out right like that great and now we're going to click log out right here and you can see I'm logged out amazing job and you can try again let's see like this so I'm gonna write it antonio.com123321 click continue and you can see how fast this loaded because we immediately fetch the current user from the database from our layout server component amazing job I think this is really cool what we were able to do with next routine and server components and we're gonna do the very same thing to load our listings there is not a single get API endpoint in this entire project because we do not need them at all that is amazing in the next part of this tutorial we're gonna finish our authentication by enabling Google and GitHub login so before we add our social logins with Google and GitHub I want to bring your attention to something let's log in and now what happens is we used a server component called layout right here to get our current user from the database and we passed it to our client component right here and it seems to work just fine all right we are getting this conditional different options if we are logged in but if you take a look into the console right here you see we have a warning only plain objects can be passed to client components from server components date objects are not supported so let's head into our Prisma schema right here and let's take a look at what are some of the date options that we are passing so we have email verified created at an updated ad so in order to get rid of this error which can cause errors with Hydra hydration we have to do the following let's go back and let's close everything and let's go into app actions get current user right here and instead of just returning a planed current user we can have to modify it a bit open an object and spread the current user inside and now first thing we're going to modify is created act so write created at is going to be current user dot Creator at dot two ISO string like this and next one is going to be updated at so go ahead and write current user that updated that to ISO string as well and last one is going to be email verified which is current user dot email verified which is optional so you have to write a question mark dot to ISO string like this pipe pipe now like that and that is the way we want to pass our objects so now let's take a look at something let's go into layout right here and you can see we get a typescript error because created at an updated that an email verified are strings but in navbar component if you enter it you can see that our user object expects email verified to be date as well as created at an updated ad so we have to create a save type using this Prisma client user in order to do that let's open everything and let's go into app and create a new folder called types and inside create a new file called index.es and now let's create our safe user so go ahead and write the export type save user is equal to Omit open point in Brackets and inside in the first parameter I'm going to write user from add Prisma slash client right here and we're going to meet created at pipe updated at pipe email verified and we're going to replace those values with our custom ones which are created at which is a type of string updated app which is also a type of string and email verified which is a type of string or null like this and now we can use this save user instead so go back into components navbar navbar right here and replace user type with save user which you can import from App slash types like this and you can remove import user from Prisma client like that and that fixes our error here but we're still passing current user to user menu so go into navbar user menu here and do the very same thing replace user with safe user like this then you can remove the user from Prisma client and just make sure you import save user from App slash types right here and now if you go into your terminal you can see even if we refresh our warnings are gone because we have successfully sanitized our current user so we are not passing any of the dangerous objects like date and everything should work exactly the same because well technically we just modified the types and uh we modified the way we return our action here in a very very simple way so just make sure it looks exactly like this and this current user email verified is optional so make sure you put a question mark dot to ISO string pipe pipe null at the end great and now it's time to enable our social logins and for that let's do GitHub first because it's much simpler so I'm going to log out for a second and I'm gonna go into my GitHub right here and I'm gonna go into my settings and I'm going to go all the way down to developer settings right here now I'm going to go and click all out apps here and I'm going to create a new or out the app and I'm going to name it Airbnb Dash video home page URL is going to be HTTP localhost 3000 like this and just copy that for authorization callback URL as well and click register application great and now we have our client ID right here so let's go into our environment here and let's also open our Pages next out right here so we need GitHub ID and GitHub secret so let's add that here so for GitHub ID we can use this client ID right here and click generate a new client secret to get the client secret and then you can copy the client's secret and paste it here like that great and now let's go back into app components and first let's do the register model so instead of these empty functions here we're actually going to replace this with sign in from next Dash Outlet slash react so make sure you import sign in from next dash out slash react right here and let's go here to the bottom and the sign in you can just write GitHub like that and now let's take a look I'm gonna click sign up and I'm gonna click continue with GitHub and what that does it opens the authorize window and I'm going to click authorize my name and after that you can see that I'm successfully logged in and now let's take a look at here and let's refresh everything here and you can see I have a completely new user here which uses my email from GitHub right here and I also have a new account for GitHub right here and one cool thing you're gonna notice here is that we actually have an image from our GitHub so I'm gonna go ahead and I'm gonna pass our current user image to our Avatar component right here so let's go into components navbar user menu and we already have the current user here so all we have to do is find our Avatar and let's pass in this source to be current user question mark dot image like this and now we have to add this source to the Avatar component your avatar component is in components Avatar right here so let's write the interface Avatar props they have source which is an optional string like this and just pass this props here and let's just write the source like that and we're going to write a conditional here so we're gonna either use a source or we're gonna use this placeholder like that and let's just also add a couple of more types here so don't make it like this make it string type null pipe undefined like this so it fixes our type error here and you might notice that we get this error avatars that GitHub user content is not configured under your images in next dot config.js so let's go ahead and copy this avatars.githubusercontent.com I'm going to zoom in so you can see we have to add this hostname to list of our allowed host names so close everything and go into next.config.js right here and outside this experimental you're gonna add images open an object and write a domains and that is going to be an array and the first thing in an array is going to be Avatar that githubusercontent.com and you can save and every time you modify next.config.js make sure you shut down the application and run it again because it's not going to get updated by hot reloading so just refresh your localhost 3000 and just like that you can see that I'm using the image from my current user because I signed it with GitHub it automatically added the image and I can now use it here in user menu in my avatar component great great job and now we're gonna do the same thing for Google but before we do that I'm gonna add remember I just added this in app components models register model so I added this sign with GitHub only here but I also want to add it in my login model here so go into login model footer content and just do the same thing for GitHub so we use this sign in with Git Club there as well all right and now let's do the Google version of this so I want you to Google Google developer console and click on console.cloud.google.com like this and you will be greeted with probably a similar screen you can see I'm here from my last tutorial which is a Netflix clone so all we have to do is Click create new project right here I'm going to name my project Airbnb Dash clone organization is fine and click create and just wait for a second for this project to complete and just click select project right here if it doesn't offer you that you can search for your project here make sure you are in Airbnb clone like that and now you can search here for API like that and just click on enabled apis and services here great and now what we're going to do is first we're gonna create an all-out consent screen so click on the sidebar here on oauth consent screen and let's click external so it's available to any test user with the Google account and click create for the app name I'm just going to write Airbnb clone user support is going to be my email you can leave all of this blank for now and just make sure you fill the developer contact information and click save and continue you can leave everything exactly at ease and click save and continue here as well and save and continue one more time and that's it and now you can click on credentials right here great and now you want to click on create credentials right here at the top and create uh oauth client ID for the application type select a web application name can be web client one and now let's just add authorized redirect Uris so go ahead and write HTTP slash localhost 3000 but it's not going to be just this right slash API slash out slash callback slash Google like that and just press create and now we have our ID and our secret so let's close everything here and let's go into Pages next out to see what we need we need Google client ID and Google client secret so let's add that in our environment file right here Google client ID and Google client Secret great and let's copy the client ID from here let's paste it here and let's do the same thing for client secret right here great and now before I do this I'm going to log out first so always make sure you're logged out when testing this all out logins and because I'm using the same email uh right this is my also my email uh for Google I will actually delete all of my accounts and my users so I'm gonna head and delete this GitHub user right here you don't have to do this if you have different emails but I'm just gonna do it so no problems arrive and I'm gonna remove this account as well great I'm gonna refresh here one more time and now I'm gonna try and actually it won't work why well because we have not added anything so let's go into our register model cell models register model and just the way we did with sign in GitHub we can replace this empty function with sign in Google like that I'm just gonna refresh just to make sure everything is up to date and I'm gonna click up all right let's open sign up again and click continue with Google and you can see that I have my account right here I'm gonna select it and guess what we get another error why well because we have a new image this time from Google content so I'm going to zoom in a bit again you can see it says the same thing it did for GitHub hostname lh3.googleusercontent.com is not configured under our next config.js so just go ahead and copy this and go back into your next.config.js and add that as well and as always remember to shut down the application and run Dev again and then you can refresh your localhost and just like that you can see how it now loads but the very same image I have in my Google Chrome right here amazing amazing job you finished oauth and one thing that's left to do is to add the sign in to login model as well so go into your login model in components models login model and just change this empty function with sign in Google like that I'm just gonna refresh this now I'm gonna click login right here and click continue with Google and you can see I'm back and logged in amazing amazing job and one thing you might notice if this ever happens to you you can see I have a quite of a different font now uh that's because nunito failed to load that this happened to me a couple of times while using the app directory all you have to do is click on your next dot next right here and just remove dot next and also make sure to shut down the application and just run end game Run Dev again refresh your localhost and that will create a new DOT next folder for you which is basically next.js cache and you can see that my font is back on if this didn't happen to you if your phone stayed exactly the same throughout this tutorial no problem you don't need to remove this dot next file but if you ever lose your phones and you cannot get them back for any reason you can always just remove dot next this is just a cache and then you can run npm run Dev again great great job you finished authentication completely in the next part of the tutorial we're gonna start exploring uh listing creation so before we move on to listing creation I want to create a categories uh field right here just below our nav bar so let's go ahead and let's head into our navbar navbar.tsx right here and just outside this container and outside of this div create a new field categories just like this and if you save we're gonna get an error because categories is not defined so let's go ahead and let's fix that by creating a new component inside our navbar so create a new component inside components navbar folder called categories not DSX like that great and let's name it categories and just return an empty div now we can go back into our nav bar right here and import categories to fix this error right here you can import categories from dot slash categories because they are in the same folder all right and now what I want to do is I want to add some styling to this so first let's use our container component which you can import from dot dot slash container like this and inside that create a dip and give you the following class names so pt-4 Plex Flex Dash row item slash Center justify Dash between and overflow Dash X slash Auto like that great and we are not seeing anything yet and that's because well you can actually see a small field here maybe you can see it better on your own browser than on mine so what we have to next is we have to define a list of all of our categories so let's go ahead and let's do that so right here I'm going to create a constant which I'm going to export because we are going to reuse this categories constant um in some other places as well so let's go ahead and write export cons categories is going to be an array of objects and the first object is going to be Beach so go ahead and write Beach like this and the item you're going to use is TD Beach and we can import PB Beach from react Dash icon slash TV like that great now uh let's also add a description which is going to be this property is close to the bridge like that and we're gonna use this uh to create our second category as well which is going to be windmills and it's going to use an icon GI windmill and you can import that GI wind you know from react Dash icons slash GI like that great and let's change this to this property has windmills so we're just adding some content so our site doesn't look as Bland then we're also going to add modern and we're going to change this to MD outline Villa like that which you can also import from react Dash icons slash MD like this and let's give this a description of this property is modern for example okay now we have three of these categories and before we go ahead and add all of the categories which we're gonna have uh I actually want to map over this three and show you uh how that is going to look like so go ahead and write categories.map item up and just go ahead and return a category box which we are going to create and inside of that first DB tech key which is going to be item.label and then pass it a actual label which is also item.label and then pass the description which is open that description and pass in an icon which is item dot icon like that great and if we say we get an error we just category box uh does not exist so let's go ahead and let's create a category box inside our components because we're going to reuse it so create a new file category box dot PSX like that let's just name it category box like this and return a div and now let's go back into components navbar categories here and let's just import category box from dot dot slash category box like that and save the file great and just refresh one more time all right uh and now let's actually style this category box so it looks like something all right so I'm going to go into category box right here and first let's create the interface for it so interface category box props it's gonna have an icon of Icon type which you can import from Yak Dash icons uh it's gonna have a label which is a type of string uh and for now description is not going to be needed in this part great and let's just assign this react.fc if you have category box props and let's extract these values so icon and label and I'm also going to add another one I'm just going to be selected which is an optional Boolean like that and let's also use that as well great all right and now it's actually style this so in this div let's give it a few class names so class name it's going to be Flex Flex call items slash Center justify Dash Center the app dash 2p-3 border Dash B-2 hover backslash neutral Dash 800 transition enter sir Dash pointer and now we're going to use this selected to add some optional ones so for that we have to replace this class name annotation strings with curly brackets and add annotation strings inside like that and now we're going to get conditional classes so selected is going to create a border Dash B neutral 800 otherwise border is going to be transparent like that so if we have selected it border is going to be neutral 800 otherwise border is going to be transparent and another selected optional class name is going to be text Dash neutral Dash 800 and otherwise it's going to be tax Dash neutral Dash 500 like that and now inside what we're going to put is the unactual icon so in order to do that we need to give an alias to our prop icon right here so let's go ahead and write the icon in our props right here so put this double dot right here and a capital icon so we can use it as a component icon with a size of 26 and it's a self-plausing tag like that and you can now see we have our three icons they are very far apart but don't worry uh when we add more of them they're gonna look much much better and below that create a div with a class name uh font Dash medium and text Dash SM like this so font Dash medium and text Dash SM and inside of that you're just gonna pass in the label like that and great now we have the beach windmills and modern great beautiful all right and now I want to add um something to happen once we click on this category box right so once once we click on this category box you might have seen in the intro video that it loads new listings but the way it does that is by assigning a URL parameter here so we can actually do that right now we didn't have need to have any um listening loading logic yet we just want to test how it assigns things to our URL great so in order to do that we're gonna need to use our router and our search params so let's go ahead and add a router here so const router is equal to use router which you can import from next slash navigation not next slash router but next slash navigation and we're also going to need our parents so for that we no longer use a router.query we use conspirants is equal to use search params which you can import also from next slash navigation so use router and use search params are both from next navigation like that and make sure you execute this hook great and now let's add a const handle click method which is a use callback which you can import from react so I have imported this use callback from react right here and first let's define the current query select print query is going to be an empty object and for us to continue when you install one package called query string so let's go into our terminal right here let's shut down the application and let's run npm install query Dash string great and let's go ahead and add that here so import I'm going to use Qs from query Dash string right here if you want to you can actually write query string but I just think Qs is much shorter and go ahead and run your application again so make sure you run npm run Dev and just refresh your localhost so you're looking at the newest version all right and now in this handle quick we can continue to work and first things first we want to check uh if we have parents at all because params can be a type of null as you can see so if params in this handle click function in that case we're going to add current query to be equal to Qs dot parse arms to string like that so we are basically going to create an object out of all of our current parameters uh in the future we're going to have many parameters because we're going to store a bunch of things in our URL including uh the search location the start and end date of when we want to go on a vacation the number of guests we want to bring the number of rooms we are searching for all that stuff so we need to make sure that by clicking on one of these categories we don't accidentally remove those previous parameters we want to be able to combine all kind of parameters great and now what I want you to write further is const updated query and give it an option of any for now all right and inside of that what I want you to write is spread current query and just add category to be the current label right so when we click on one of these category boxes the current label is going to be assigned as the category param in our URL all right and now that we've done that let's actually check if perhaps uh the if we already have selected Beach for the category once I click on the beach again I want it to remove all categories right so we need to find a way to reset a category uh once we selected it and the way we do that is once I click on Windows it's going to select windmills but once I click again it's going to remove them so in order to do that we will do this check if params question mark dot get category is equal to label and in that case all we're going to do is delete updated query dot category like that so if the category we click on has already been selected in the URL that means we want to reset it so we will remove it from the newest query and all we have to do then is write const URL is equal to Qs that stringify URL and open options like this write URL to be a slash like this and the query is going to be our updated query like that and I want you to add another option right here which is just going to say skip now to be true like that and now that we have generated this URL with the newest query and this as our path name and we have filtered out all of the empty options what we have to do is router that push and pass that URL inside so it might seem a bit complicated but when you uh separate the logic it's not that much so first we Define an empty query we look through the current params and we parse them so that they are an object and not a string because by default parents to string is a string of course so and then we spread that current query and add the new category in there then we check if the new category is already selected and we remove it from our updated query because obviously we want to deselect it if we were clicking on it again think of it like a toggle on and off and then we generate the URL string using query string stringify URL where we pass our path name which is always going to be a slash and we just pass the newest query which we have manipulated right here above great and now don't forget to fill your dependency array so we are missing label parents let's see what else we're missing and router like that great and now we have to assign this handle click function to this div right here so just a little class name add on click to be equal to handle click like that great and now if you check if I click on any of this modern or windmills or Beach and check your localhost 3000 URL you should have a new ad category equal to beach or windmills ad category equals uh sorry question mark category equals to windmills or modern question mark category now equals to Modern amazing amazing job and if you click again it removes it that's exactly what we wanted great and now let's add a way that this category can actually read from the URL and and show a selected option of itself so for that we need to go back into categories right here uh and let's do that in a different way here so what we're going to do is write const parents is equal to use search params again from next slash navigation so right now we are working in the categories component we have exited our category box this is it for the category box you can go back into components navbar categories right here and use this hook use search params here and also uh trigger actually not don't trigger anything uh you need to extract the category so the way we can extract category from these parents is const category is equal to param's question mark dot get category like that great and now let's also uh find a way to hide this categories box because remember we want to see these options like modern windmills and beach only on the index page we don't want to see that on our favorites or on the actual page of a single listing so we have to limit it so we're going to do that using path name constant name is equal to use path name which you can also import from next slash navigation like that great and remember to execute that hook as well and now I'm going to add a constant const in this main page it's going to be equal to path name equal to slash just like that and if we are not on the main page so if exclamation point is main page we are just going to return null like this great and now in order to check whether we have selected a specific category we're going to use this params.cat category constant which we have created right here and I pass the description we don't need description so we can replace that with selected so instead of description right select that right here and the way we will notice if something is selected or not we're going to compare the current category pattern is equal to item.label like that great so you can see that my windmills is now selected but if I click on Modern in that case modern is selected if I click on beach in that case Beach is selected and if I click on which again then everything is removed exactly what we want great job and now it's time to go back into categories and to add the rest of our uh the rest of our categories actually so make sure your enough bar categories right here and we do have a lot of this to add but don't worry we're gonna do them together and let's just start by copying this modern right here and name this country side like this and we're going to use uh TB Mountain for that which you can import from uh react Dash icons slash TV so we already use TB Beach and now we're going to use TB Mountain for Countryside and we're going to add this description to this property is in the countryside like that great now let's add another one and this one is going to be pools and for that we're going to use the icon TB my bad you can use the icon TB pool which you guessed it you can also import from react Dash icons TB like that and we're just going to say this property has a pool like that you can go ahead and copy that as well and for that you can see how they are just keep getting adding they just keep getting added beautiful so after pools we're gonna have islands and the icon we're going to use is GI Island like that and you can import that from react Dash icon slash GI the same way we did with windmill right here and which you can say this property is on an island like that all right let's copy and paste this again and let's create this one to be a lake and the icon is going to have is GI both pushing again from react Dash icons slash GI right here and we're just gonna say this property is close to a lake great let's copy this again now after Lake our category is going to be skiing and the icon we're going to use is f8 Sting like that and you can import that from react Dash icon slash fa ith so fa skiing from react Dash icons slash fa like that great and the description is going to be this property has scheme activities like that great let's copy and paste that now after skiing we're gonna have castles and we're gonna use GI castle for that and you can import it from react Dash icons uh Slash GI so we have a lot of this GI icons here so just make sure your GI Castle is imported from react Dash icons GI alright and this property is in a hassle is going to be the description of that great after castles we're gonna have camping and the item we're going to use for camping is GI Forest Cam and again you can also import that from react Dash icons slash GI and we're going to add the description this property as camping activities like that activity like that great and then you're gonna copy that again and we just have a few left so after camping uh we're going to add Arctic and the icon is going to be the as snow and you can import BS snow so import bsno from react Dash icons slash BS like that all right now copy the Arctic and let's add a cave category now our cave is going to be GI K entrance so make sure you import GI cave entrance again uh I'm just gonna collapse all of this so these are all of the icons we have so from GI so GI cave entrance from react icon slash GI is the newest one all right after cave we're gonna have desert and the icon is going to be GI Cactus again we're gonna add GI cactus from react Dash icons GI and let's just fix the description for cave so description is going to be this property is in a cave like that and for Desert is going to be this property is in that desert like that all right and let's copy and paste the desert and after desert our category is going to be Burns and just one more left after that so use GI Barn icon for that again GI Barn from react Dash icons GI all right and just say this property is in the barn like that and the last one is going to be Lux and the icon we're going to use is IO diamond for that and you can import IO diamond from import IO diamond from react Dash iTunes slash IO like that slash io5 my apologies so react Dash icons slash io5 and that's how you can get the i o Diamond icon right here all right save that and we're just gonna say this property is luxurious like that great and now we have a lot of uh categories here great this is going to be great because we can create a lot of listings with a bunch of different categories and you can see how it looks even on a large screen it does not look bad amazing and you can try on any of those and you can see how each of them gets selected because of the URL control and you can select in one of them again to see how everything gets cleared great and on smaller devices you can see uh how we can actually scroll inside all right and before we move on I just want to add a couple of things so go ahead and close everything and first I want to address something from the last part of the tutorial in the login model so let's go in in the app components models login model right here and I made a mistake here so when we created default values for this let me just open login models so it's clear what we're talking about we have email and password field right here but in our default values I all I use name and password so why does this work why are we not getting an error well it does not matter what we write in our default values right because we are using an input here which has an ID of email so no matter if the default value is name what gets sent in this assign in function is actually what's triggered in the input and in our case that is email and password so that's why this was working even if I defined the name here so just to make this proper I'm going to rename this to email so in login model default values should be email and password it isn't going to change absolutely anything and your code would work even if you have name here or whatever you want but let's just keep things correct and do that and I'm just gonna test with my created account and you can see that it works and I am now logged in great and just before we move on I want to add another option to reset these categories right so right now the only option to clean the category is by clicking on the category again but I want to add one universal way and that will be by clicking on the Airbnb logo right now that does nothing so let's go ahead and let's go into app components navbar logo right here and here we have the unused router from next slash navigation so let's just add on click on this image and write an arrow function rather that push and it's just going to push to an empty slash function great so now if you select camping for example you can see your url should have a question mark category equal to camping and if you click on Airbnb everything should reset amazing amazing job we are now ready to start working on our rent model which will be accessed from this this button or this button right here and then we will be able to finally create our first listing and before we wrap this up I just want to make sure that we add use client to our newly created components so let's go and close everything and we have a couple of new components so we have the categories right here so go into categories and just write use client right here at the top and then go into category box and write use client at the top as well the reason it was working I think is because we use category box and categories inside of client uh parents so I think that makes them client as well but just to be sure I'm not 100 sure how the new app router works I suggest that you add use client to category box and use client to categories as well great job so let's continue by creating our rent model which will be triggered using this Airbnb your home button or Airbnb my home right here in the user menu so uh before we get on to that I just want to bring your attention that we still have some unfinished things in the login and registration model which I just want to wrap up so once we click login if we click on already have an account you can see that our login model asks us to log in again so we have to fix that so let's wrap everything up and let's go into app components models and login model right here and you can see we have our register model and our login model here so I will just separate this like that and let's go into our further content right here and let's change this message for the login model so make sure you're in your login model right here and uh instead of already have an account the question we want to ask on the login model is first time I'm using Airbnb question mark and now you can see that this question changed and instead of offering the users to log in we want to offer the user um to create an account like that great so now we have the question the first time using Airbnb create an account and instead of uh triggering the register model to close we're going to create a toggle function here so just above the body content here let's go ahead and let's write const toggle and it's going to be a used callback method which we already have imported from react right here and what we're going to do is we're going to shut down the login model and open the register model so let's go ahead and write a login model dot on close and register model dot on open like that and let's just pass both login model and register model in dependency array of our use callback right here and then we can use this toggle function and pass it here instead of a register model on close in our create an account underlined text like that and now if you click create an account you can see that our login model closes and our register model opens and we want to do the exact same thing in the register model so you can go ahead and copy this toggle function that we created right here and let's go into models register model right here and we're going to paste it uh just below on submit and above body content and instead of closing the login model we're going to close the register model here and we are going to open the login model now you probably notice we have this red underline saying that login model does not exist and that's because we never added it so let's go right here and write const login model is equal to use login model write that and I will just put the Imports together so it's easier for you to know this world previous great and now if I try on register and click login well something's wrong let's just refresh to make sure everything uh we know what is wrong well we added the toggle method right here but we never added it to the actual login clickable div right here so let's go into our footer and where we ask already have an account instead of on click register model on close we're going to call toggle like that and now if I click on login you can see that the register model closes and login model opens and vice versa on the login great I'm pretty sure that is it for our Authentication and now you want to make sure that uh once we click on this Airbnb or home actually we want to make sure that we are logged in so let's go ahead and let's do that in our navbar right here so let's go into components navbar and let's go into user menu right here which actually has the current user and we're going to pass a function to this empty on click that we have and we're going to call that function const on rent like this use callback which we already have imported from react and we're going to check if we have the current user so if there isn't a current user uh we're gonna call the login model that on open like that and just make sure you return this so it breaks the other part of the code right so if you just write it like this then we're going to continue and execute the other part of the code that we don't want that to happen if we notice that there is no current user we want to return this and open the login model and otherwise I'm just going to write a comment here open rent model which we will do in just a second and make sure you pass the current user and login model in the dependency array of this use callback and now let's use this on rent function right here and replace this empty function which says Airbnb your home like that and now since I'm logged out if I click Airbnb at home you can see that I'm triggered with the login right here so I'm going to log in here and we're going to start creating our rent model so now if I click nothing happens because well we just added the comment open rent model so let's go ahead and let's go into our hooks and let's just copy login model and let's rename this hook to use rent model like that and just just as always let's rename login model to rent model like that so everything else is exactly the same the control is is open own open and on close like that great and now I'm gonna close everything and I'm gonna go into components models and I'm going to create a new file rentmodel.dsx right at and I'm just going to name it rent model like that um and all it's going to return is a model which you can of course import from dot slash model like that and let's give it a specific title so title can be Airbnb your home like that uh now our model is missing a couple of stuff like is open so let's just add that to the rent model is open like that and for that we need to import models all right constant model is equal to use rent model from slash app slash hooks use rent model because we just created that here in the hooks user and model make sure you export the user and model all right and now that we have the controls for the user and model uh we can safely control it uh like that and we're gonna add on close which is rent model dot on close and we're gonna add on submit which is rent model on submit uh like that sorry uh on close again let's see what else do we have in required we have action label action label is just going to be submit for now like that uh and now we're still not seeing anything here so let's go ahead and let's go into our uh layout.vsx and where we added login model and register model we now want to add our rent model as well which you can import from dot slash components slash models print model let's just keep our models together like this all right uh and now what I want to do is I want to go back into user menu component uh yeah oh we have a mistake here so we have not named our rent model a client component and since it's using a hook it needs to be a client component so just write used client on the top of your rent model like that great and now the error is gone and now let's go back into navbar user menu and instead of this comment open rent model we're actually going to open the rank model so for that we need to add the hook here constant model is equal to use rent model like that which you can import from slash app slash hooks use rank model like that I'm just going to add that here I like to organize my stuff like this all right and then we're going to use this rent model to replace this comment right here rank model on open like that and just added the dependency print model like this great and now if you click Airbnb or home you can see we have this new model and we can close it very very nice and we also want to make sure to add it to this option Airbnb my home right here so let's scroll all the way down where we have this current user conditional and Airbnb my home is just gonna going to trigger rent model on open like that so now we can both open it using this button and we can open it using the user menu right here excellent all right now let's go back into our rent model so go back into app components model rent model right here and first I think I want to do is I want to create an email NM for our steps because our rent model is going to have a couple of steps first we're going to select the category then we're going to select the location then we're going to upload maybe some images we're going to select the number of rooms the number of guests the number of bathrooms that our listing has and then we're going to add some title and description for it so let's go ahead and let's write the steps so and steps and you can just open an object and first one is going to be category so we're going to map that to zero second one is going to be location so we will map that to one info is going to be the third one uh images is going to be the fourth one description is going to be the fifth one and price is going to be the final and the sixth one so my bad the price is equal to number five so make sure category is zero location is one info is two images is three description is four and price is five uh a great great job all right and now let's add the control for these steps so go ahead and write cons step set step is equal to use State and the default is going to be steps dot category so this is going to default to zero and you can import your state from react go ahead and import that like that okay and now that we have that let's create our functions uh which will go backwards or forwards so const on back you can just open a function like this and write set step value all it's going to do on back is value minus one so whenever we click on back we're just gonna go minus one and const on next is going to be also a very simple function we're just going to take the current value and just return well you can already guess Value Plus one like that all right great so we have the on next here and now let's write uh our action labels which we have hard coded here but it's actually going to be different so we're going to have an action label and we're also going to have a secondary action label so we're going to write some constants for that so const action label is equal to use memo foreign we're gonna check if the step is equal to steps dot price so if we're on the last step because you can see step price is our last step which means that user has filled everything before that uh in that case we're going to return uh create because we want to say the user hey create this otherwise we're going to return it next because there is additional steps following and make sure you add step to the dependency array here great and you're also going to write cons secondary action label which is also use memo and it's also going to use step inside you can already write that and first thing we're going to check if if step is equal to steps dot category so if we are on the first step then there is no back button so we can just return undefined and otherwise we can return it back right great uh and now let's just apply those so let's go right here and change the action label uh to action label and let's add secondary action label to be secondary action label great and now let's add secondary action to check whether we are on category so step is identical to steps.category so if that is true then the secondary action is undefined and otherwise it's on back so secondary action we're going to check if we're on the first step and then we're gonna make sure that the secondary action does not exist because there is nothing to offer the user and otherwise we will offer on back like that great and now let's actually keep this open so you can see how this looks um great and now let's create our body content here so we're gonna have our body content be dynamic depending on the step that we are on so instead of const body content we're going to write let by the content because it's going to be a changeable variable great and inside of that let's write a div and let's write class name is equal to Plex Flex Dash call gap-8 and inside of that add a heading which you can import from dot dot slash heading which we already created in some of the previous parts and let's give it a title of which of these best describes your place question mark and subtitle of pick a category like that uh and let's add this body content to body in the model like that great and now because we because we are on the step one uh our body content is uh this heading right here great uh and let's add a div here which is actually going to be a grid so last name is going to be grid grid Dash called slash one and the grid Dash course Dash two Gap Dash three max Dash H dash 50 the H overflow y Auto like that and what we're gonna do inside of that div is we're going to map our categories so let's go ahead and write categories like this which you can import uh so make sure you import the lowercase categories from dot dot slash navbar categories I'm going to explain what that is so your input should look like this so you should import destructed constant categories from navbar categories component why does this work well remember in this component we have this array of categories where we added the icons and descriptions so just make sure you export that constant so it does not import your categories component which is the wrong thing we do not want that we just want to reuse all of these options here so make sure you have export cons categories so you can import that array like this and then you can use that array to map over items so go ahead and write that map item and for now what we can do uh let's just open like this and let's write a div inside oh my bad okay so we have a div here uh let's give it a key of item.label and a class name or call Dash span dash one like that and for now let's just write item.label great and you can see that we now have a list of our categories here again if it's not working for you uh make sure your import looks like this and make sure your uh Slash nav bar slash categories component which is located so in components navbar categories right here make sure you export const categories array here great uh and now uh because we have that we're gonna have to create a new component called category input so let's go ahead and let's replace this with category input this item.label so category input which is a cell closing tag it does not exist yet but we're going to create it and for on click you can just pass an empty function for now uh for selected you can just say pause for now label is going to be item.label and icon is going to be item dot icon like that uh great now we get an error because category input does not exist so let's go into our components and since it is a category input let's create it in our inputs so new file category input that's vsx like that let's write sfc category input and just return an empty div for now and go back into your rent model and just import category input from dot dot slash inputs category input and save the file great so now the error is gone and let's go ahead and let's first things first Define this component as use client because we're going to use some hooks here uh and now let's write the interface interface category box sorry category into props uh it's gonna have an icon of Icon type which you can import from a react Dash icons right it's going to have a label which is a string it's going to have selected which is an optional Boolean it's going to have on click which accepts a value which is a string and returns a void like that and make sure you attach those props to this component so category input props and just extract all of those so icon which you can already remap to Capital item like that because we're going to use it as a component label selected and on click like that great uh and now let's write our div classes here and you can actually open the rent model just so you can see what you're working with here great so uh on click is going to be an arrow function which goes on click and passes in the label like that last name is going to be a template little string like this because we're going to be using some conditional classes here and go ahead and write some static ones first so rounded Dash XL border Dash 2 e-4 Flex Black slash all Gap Dash three hover border Dash black transition cursor Dash pointer and now we're going to do the conditional selected border Dash black otherwise it's going to be border Dash neutral Dash 200 great and you can see how we have this little tabs right here and we automatically have our selected uh uh hover here sorry not not selected style we have our hover style here great and inside of that we're going to add this icon you don't have to import that from anywhere because we are using it right here as an alias for our icon which we pass from the categories array and icon is going to have a size of 30. now you can see now we have all of our categories here great beautiful and let's add another div here which is just gonna write this label here and to make it look a little bit better just write class name on semi old like that great and now we have our categories and you can also collapse this to see how it looks on mobile and you can see it's completely functional on mobile as well all right uh so make sure you export this category input here I mean if you're already seeing it then obviously everything is working uh just fine great and before we move on I just want to connect the option of this select into our form so in order to do that let's go back into rent model right here let's go to the top and we're gonna have to initialize our form the same way we did in the login and register model so const and we're gonna extract register handle submit set value watch form state which destructs uh errors like this and reset like that and you're gonna get that from use form which you can import from react Dash hook Dash form great and use form is going to work with field values like this so make sure you add filled values inside these pointy brackets and you can import field values from a react hook form as well and open a object like this so open a function and open an object inside and we're going to write default values to this form for the rent model so we're going to have a category which by default is empty we're going to have a location which is going to be an object so we're going to write null we're going to have the guest count which by default is going to be one room count which is going to be also one bathroom count as well image source but I write SRC and make sure you write SRC as well because that's what we named it in the database in the Prisma schema so just to avoid any problems or you can go and check your Prisma schema uh right here in Prisma schema check your listing so we are building a form to create this listing and just check if you're using image source make sure you pass the image source as well to save yourself any problems uh price is also going to be one initially title is going to be an empty string and description is going to be an empty string as well great uh now if you remember from our register or login model we would usually pass this register extracted uh constant into our input and that would make it work and it would just magically be connected to the form but because in our case we created this custom category input we have to create a workaround so first things first we have to create a way to watch our category uh value so let's go ahead here and just write cons category is equal watch and in parenthesis here right category like that so uh make sure you just write category here category here category here and this watch is exported from this useful it's distracted from this use form hook right here great uh and now what we have to create is a custom set value so we have this set value right here so you might be thinking oh well I just assigned this on click to the set value but in react form hook set value by default it does set the value but it's not it does not re-render the page so we have to motivate a little bit so go ahead and write const set custom value is equal to a function which accepts IV as a string and value as any because we're going to work in with a lot of values here and then we can just call this set value from use form so set value is going to accept an ID and value and open the third parameter which are going to be options and these options are going to be showed dirty true should touch true and should validate true so make sure you you have should dirty sheet touch and should validate should validate is definitely the most important one so make sure you add should validate uh to be true uh okay and I'm going to use this set custom value here and we're going to modify that in our category input so go right here to the bottom and here on click we're going to extract the category like this and we're going to call uh set custom value we're gonna pass uh the ID which is category and then pass sorry the category which we extracted here okay so I will just collapse this like that so on click our category accepts a category which is a string and then we call set custom value which remember first accepts the ID and then accepts the value so we have to Define what is the ID of this input the ID of this input is category great and then what I want to do is this option selected we have to modify it so instead of false it's going to be category equals item dot label like that so what do we get this category from well we get it from this constant watch category like that all right so now let's go ahead and let's try and select Beach for example and you can see that beach stays selected if I try Islands then Islands stay selected if I try pools then pools stay selected barns desert Lux and everything else great so we have successfully connected this category in component with the entire form and now we will use that in our final submit function to actually pass the proper category great in the next part of the tutorial we're going to create uh the second step of this input which will offer us to select a location great job so in this part of the tutorial we're going to create step two of our listing creation form also known as rent model for us in the code so our Step One is the category input list right here which we created in the last part of the tutorial so now what we have to focus on is this step two location so let's go here where our body content is and let's create an if Clause right here if step is equal steps dot location and instead of return we can now write body content is equal and open through indices here and let's just write that div which is going to say location step like that and now let's just replace this on submit right here uh with on next like that we defined this on next here in the beginning which just increases the step by one we're gonna change this on submit later um to actually uh uh submit form to our post request but for now we can just keep it to load the next step so once I click next you can see that I am now on location step and once I click back I am back in my category so we can go next and back great uh and now let's actually write the code uh for our location so inside of this div add a class name which is equal to Plex Flex Dash call gap-8 and inside of this we're going to write a heading which we already have imported here so this heading is also used here so let me give it a title of where is your place located and a subtitle of help yes find you like that so now if we go to step two you can see that we have this new heading right here great and just to confirm you can import heading from dot dot slash heading right here at the top all right now let's see what else we have to add in this location step So Below this we want to add an input to select uh our location so let's go ahead and write that that is going to be called country select like this but for now it's not going to have any values here and we're going to get an error if we try and save this file so let's go in components inputs and create a new file called country select dot PSX like that and let's just Define use client at the top and let's just fix the error so country select and let's just return a div like this and let's go back into models runs model and import country select right here at the top dot dot slash input slash country select great and now I'm going to open uh this again and you can see we no longer have an error we just have a well an empty space because this is just an empty div so go back into inputs country select and we're going to start working uh on this now so first thing we need is to create a hook called use countries uh that will load all of our countries so let's go ahead and let's close this components folder and let's go into Hooks and create a new file use countries.ps all right and that use countries hook will use a package called world countries so let's go into our terminal I'm going to shut down the application and let's write npm install world dash countries great now I'm gonna import countries from world dash countries and that is it we have the types all right so you don't have to start the application yet because we're gonna have to install another package but let's just finish writing this hook so first let's define formatted countries which we're going to use in our select input which you will see in a moment so cons formatted countries like this is equal to countries which we have from this world countries.map country and let's just see so countries I made a mistakes here so countries like that make sure these countries Maps the import from here so countries.map and we're going to return an object so make sure you open parentheses like this and inside write an object and we're going to remap some of the values so value is going to be country Dot cca2 label is going to be country dot name dot comment plaque is going to be a country dot flag uh lat length is going to be country.let and LNG which is sort for latitude and longitude and region is going to be a country that region great and you will actually get autocomplete for this certificate type region you should get a region like this same for cca2 so if you type country dot you should get cca2 Auto completed here because we have the types in countries right here great so we now have formatted countries and let's create this hook now so conscious countries and first let's define the function called get all so const get all all it's going to do is just return formatted countries like that and then we're gonna get another function get by value so const get by value is going to accept a value which is a type of string and we're going to return permitted countries that find item item that value is equal to Value like that so what we're going to do uh here my bad like this so basically what we will do in a getby value is we're going to search the formatted countries map and we're going to find an item which value matches the value which we will pass in this function when we use it in the future great and let's just return an object which exposes that all and get by value like this and Export default use entries great so that should be our hook and now let's go back into uh inputs so components inputs country select right here and let's finish writing this hook so for that we're going to need another import select from react Dash select and we need to install this package so let's go back into our terminal and run npm install react Dash select great it has the types and now we can go ahead and run npm run Dev and just refresh your localhost so you're seeing the most up-to-date stuff great now let's uh the export and Define a type for the value which this input will accept so export type country select value is equal to flag which is a string like this flag which is a string playable which is a string as well let L and G which is an array of numbers region which is a string and value which is a string as well great and now let's create an interface so I'm just gonna open my model again just to have it here no you don't have to but uh it will be good for you to see the changes you're doing now let's create an interface for this country select here so interface entry select country select props is going to accept a value which is optional and the value is going to be well this value right here country select value like that and on change function is going to have that value as well can't we select value and it's going to return a void like that and now we can assign that so react FC country select props and let's just extract these values so value and on change like that great and now inside first let's use our newly created hooks so cons we're going to extract get all from use countries like this and you can import use countries from ad slash app slash hooks use countries like that if ad isn't working for you you can go ahead and use uh dot dot slash abbreviation like that but I'm going to stay with a hook like this great so now we can fetch all countries here great and now inside this div inside let's actually use our react select here so I'm gonna go ahead and write select like this it's a self-closing tag and I'm gonna give it a placeholder of anywhere I'm gonna make sure it's clearable so it's clearable you can see we already have our little select here but currently it has no options we're gonna fix that in a moment options are going to be well this get all function here so just go ahead and assign this get all function and execute it like that and now if you try you can actually see that we have all of our countries here so I can search for Croatia I can search for United Kingdom United States I can search for Greece great uh and now let's continue modifying our select here so the current value is going to be value whatever value is passed in our props right here on change is going to have a value right here and it's going to be an arrow function which is going to uh call the prop on change right here and pass this value as country select value like that great now below that um we're gonna write a custom label a custom option label for our select so go ahead and write format option label like that it's going to accept an option which is a type of any and it's going to return uh some jsx so let's go ahead and let's write a div inside like this all right and it's going to have a class name of Plex Black slash row items that are Center and gap-3 and inside of that create another div we're just going to use this option right here option dot flag all right and uh below that we're going to create another div we're just going to write option dot label and we're going to add a comma here and a space and open another div below that uh my bad so option.label comma space and below that write a span which is going to hold option that region like that and give span a class name of that slash neutral Dash 800 and ml1 like that and now if you try you can see that we have a much better looking um much better looking uh search not with the looking options if I search for Croatia you can see I have the little flag but what I want to do is I want to show that one of these labels is a bit darker than the other so I'm going to change this text neutral 800 to maybe 500 like that and yeah that looks much better so now the main focus is the country but we can also see the region like America's Europe Asia great great great great job all right and now that we have finished our format option label let's write some class names here so it matches the inputs which we have created in our login and register functions so go ahead and write class names here sorry class names here and let's write control and just expose p-3 border Dash two all right and you can see how it enlarged our entire control great input is going to have a class name of backslash LG and let's just make sure you write a comma here a comma here an option is going to have text LG as well great and now you see my our text is much much better and much larger looking great and now you can see we have this blue uh color here so let's just change that as well by uh using the prop called theme so we're gonna accept theme like that uh and we're gonna return a function which returns an immediate object and we're going to spread the current theme inside like this and then we're going to write border radius to be six and colors inside of that object we're going to spread theme dot colors so we preserve all the other colors but we're just going to change the primary color to be black and the primary 25 is going to be hashtag ffe4e6 great so now our input matches the other inputs which we have on the website and you can see how it uses this reddish color that matches our Airbnb color great great job so of course we had an error because currently we cannot submit anything yet great and that is actually it for our country select great great job and now let's go back into our rent model so go ahead and go into components models rent model right here and Country select should probably be red right now because of course we don't have anything to change here so let's go ahead and rightly on change function here I'm just going to accept a value and we're going to run set custom value and the ID is going to be location and we're going to pass the value like that so the same thing we did here when we passed the category set custom value we pass the ID of category and the value category so right here we're using the ID of location and passing the value as well great uh and now right here where we used a category let's copy and paste that and write location and watch location like that and now we can use this location constant to assign it as a value to our country select like this so value is going to be equal to location great and now if you save then try to select Croatia you can see that Croatia is selected I can go back and I can select something here go forward and Croatia stays selected I can go back and skiing says stays connected here as well great great job but one thing we are missing here is the map so that's what we're gonna add now and you can also as you can see you can clear Croatia if you want to I think this is really really cool great now below that we need to add our Mac so let's go ahead and write map like this let's save and of course we're going to get an error because Mac does not exist so let's go ahead into our components and just in the general root component so no need to create an input or models of number just in components create a new component called map that's the SX inside great and let's mark it as use client and let's name it map now let's just return a empty div and Export default map like that then go back into rent model and just import map the same way you did with country select so you can import map from dot dot slash map great and now if you open the rent model again well nothing should appear here but you should not have an error great and now we have to install a couple of packages here so let's go ahead in our terminal I'm going to shut down the application and let's first write npm install leaflet and then let's install the types for the leaflet so npm install Dash capital D at type slash leaflet great and then let's install react lip LED so npm install react Dash leaflet at that great and now we can go ahead and run the application again and just refresh your localhost if you shut down the application grid now I'm going to add some imports so import L from leaflet like that I'm going to import map container from reactly flat and I'm also going to put marker and tile layer like that great and now I'm gonna import some CSS here so import leaflet slash list slash leaflet dot CSS like that import marker icon 2x from B flat Dash list Dash Images dash marker icon 2x PNG like that import marker icon from leaflet slash this slash Images slash marker Dash icon.png and import marker Shadow from leaflet slash list slash Images slash marker shadow.png like that so we need this three to make to make to ensure our marker works because this leaflet package is not exactly supported in react it's an open source project so we need to do a little bit more work to make it function but don't worry it's not that complicated uh and now we're gonna write a function here delete L that icon Capital icon so make sure the icon is capital like this that default also Capital that prototype dot underscore that icon URL like that and we're going to get an error here that the get icon URL does not exist so that actually exists but it's a typescript error so just write uh add PS ignore comment right here so we don't get that error because it's actually correct but well it's an open source project and it's not fully maintained and now let's write L that icon that default that merge options and open an object here and just write icon URL marker item that Source below that icon retina URL marker icon 2x that source and below that shadow URL marker shadow that source so basically we import this pngs right here from the package leaflet and then we assign it to the icon right here so we use this marker icon 2x we use this micro icon and marker shadow of source to re-set these options because by default unfortunately They Don't Really Work correctly great and now I'm just going to create an interface for this map so interface map props is going to have a center option which is optional and it's gonna accept an array of numbers which are also going to be known as latitude and longitude great and now I'm gonna open this and go to step two right here so you can actually see what we're writing so instead of this div we're going to use map container which we have imported right here from reactly Flat uh all right and let's actually save this to see if we can see anything right now okay it looks like we yeah okay we can see something but not uh nothing too much all right uh now let's give it a center which is going to be uh this property here so let's assign this props while we're already here react FC map props and let's extract Center and let's give it a prop of Center as L that let LNG uh expression like that pipe pipe we're going to give it a default value of 51 comma minus 0.09 you can modify this but I'm just giving it a placeholder if it happens that we have not passed anything in the center so if we have not selected any country by default we're just gonna point to the center of the map using this coordinates you can modify this coordinates however you want it's not required for them to be exactly this all right uh and now I'm going to add a zoom which is also going to depend on the center so if we have some uh some latitude and long we're going to zoom in a bit more otherwise we're going to zoom out like that all right I'm going to select another option here uh scroll will Zoom is going to be false and we're going to give it a class name of H dash open Square work it's 35 wbh and rounded LG like that all right uh great and right now we don't see anything yet and that is because we have to add some Styles in our globals.css all right so let's go into our app right here into globals.css and let's add dot leaflet dot bottom dot lead flat uh control dot leaflet sorry pane like this dot leaflet Dash top and we're going to reset the Z index so Z Dash index is going to be 0 and make sure you add important unfortunately we have to do this through CSS there is no other way to do it all right and now we can go back into components map right here and let's just reset our application to make sure everything is working so I'm going to since we change the Styles I'm going to shut down the application and I'm going to run npn run Dev again and I'm going to reset everything and just make sure you refresh your localhost great so let's check again if anything has changed and yes so after I refresh my application you can see that I have um this great little box right here so just to recollect everything we've done here so make sure you added these leaflet CSS here make sure you have the leaflet package make sure you have the react leaflet package and make sure you also have the types for react leaflet make sure you reassign all of these marker icons using this merge options right here great and make sure you edit this class name here and make sure in globals that you have this is the index because since you're working in a model uh it has some problems so we have to override it like that all right so why is our map Gray well because it's missing a tile layer um component which we imported right here so in order to write this entire layer we're going to need to know the URL and attribution right here so there are two options you can do this you can either follow exactly what I write here in the code or you can search for reactly flat right here in Google and you can just copy this tile layer right here because that's what you need basically we need to write this attribution which is a very long text and this URL which is also a very long text you can just go ahead and copy this style layer like this but I'm going to write it out if anyone wants to write it out here so we're going to write style layer here and I'm going to give it a URL of https dash dash we're going to open curling brackets s dot tile dot open street map dot org slash open curly brackets again Zed slash open color brackets again X it's another slash and just write y at the end dot PNG like that and if you go back you can actually already see our map working great uh if it if it's not working for you you probably made a mistake in this URL in tile layer right here so again if you can see what I'm writing here or for some reason it's just not working for you just Google react leaflet and click on the first link right here and you can just copy this entire tile layer if you want to so I'm going to show you how that looks I will just copy and paste this and you can see that uh just wait for this refresh you can see it's exactly the same but we just added this openstreetmap contributors right here so feel free to copy and paste that uh from here and you actually don't need this attribution as I've learned but just in case it changes in the future maybe you will need it all right but if you try and remove this attribution and the URL it's not going to work your map is going to return back uh to Gray all right and now we can close this actually uh and what we have to do now is well we have to pass it this Center prop so I'm gonna close everything just so it's simpler I'm gonna go into app components model rent model right here and I'm gonna go into location this here and in this map I'm just going to add a center which is going to store location question mark dot l l a t LNG like this so latitude dot longitude great and now if I try and select pressure for example uh oh yeah it's not working the reason it's not working is we have to uh we have to import this map in a specific way I know this all seems weird but yes the map we're using is not exactly supported in react so we have to do some tricks to make it work with all the server-side rendering and everything so instead of importing map like this I'm just going to copy this path right here so we're not going to import it here instead what we're going to do is we are going to dynamically import it and re-render it using this location right here so what I'm going to write is const map is equal to news memo which you can import from a react right here at the top and it's going to return Dynamic which you can import from next slash Dynamic right here all right and that Dynamic is gonna write another arrow function inside which is going to import dot dot slash map like that and I'm just gonna add a comma after this and open an object and write SSR pulse like that and don't remember to add the dependency array right here for use memo and we are going to re-render this Dynamic map every time location changes so I'm just going to paste the location right here and yes we get a warning because it says that it's unnecessary because nothing in this username depends on location but we know that our map does depend on the location so yeah we can just ignore this warning for now so basically this map import is a use memo uh which returns a dynamic import which Returns the actual import and it has an options that removes the SSR type of loading this map great so I'm just going to refresh this I'm going to go into Airbnb your home I'm going to select some pools and I'm going to write Croatia and you can see that it's zoomed in here on Croatia but one thing we're missing that I forgot is the map marker so let's go back into map component into components uh map right here and Below tile layer we're going to add a conditional so Center and end open parenthesis right here and just write marker like that and we're going to give it some crops so position is going to be Center as l dot l a t l m g expression like that great and you can see that now it points exactly in Croatia or if I select United States it points to United States if I point to India it points to India I'm going to Greece it points to Greece if I go back and go forward you can see that it's all saved amazing amazing job you just finished uh the location part of our rent model in the next part uh we're gonna add some counters and some image uploads and some other really cool stuff great job so we're going to continue by creating the next step in the rent model or listing creation model and that step is info so we're going to find our if clause for steps dot location right here and we're going to create another one just below that if step is equal to steps dot info like that and in that case we're going to change the body content the following I'm going to create a div right here put a class name Flex Flex Dash call gap-8 and inside of that we're going to add a heading again which we already have imported we're going to give it a title of share some basics about your place and the subtitle is going to be what amenities do you have like that and now if you click next right here you can see we load our info step if you go back we load our map and back once again we have the category but if you go three times uh we load our info so share some Basics about your place and a question what amenities do you have great so in order to do this we're gonna have to create another component here and that component is going to be called a counter so we're going to write counter like this make sure you don't misspell it like I do so counter like that and you can just save and of course we get the error because it does not exist so let's go into components inputs and create a new file called counter.psx like that make sure you mark it as use client at top and just write uh let's just Define this so counter empty div and now let's go back into models rent model right here and let's just import this counter from counter so you can import counter from dot dot slash input slash counter right here like that great and now I'm gonna keep my um rent model open at the third step right here so I can see what I'm doing great let's go back into counter and let's create an interface for it so interface counter props it's gonna have a title which is a type of string it's going to have a subtitle which is also a type of string a value which is a type of number and on change which sends the value which is a type of number and just returns an empty void grade now let's assign this props right here so react FC is equal to counter props and let's extract that so title subtitle value and on change like that great now I'm going to define a couple of functions here so I'm going to write const on ADD with capital A so constant add is going to be used callback and you can import use callback from react right here at the top don't forget to give it the dependency array like this and inside of that uh we're gonna basically change our values so we're going to write on change and we're going to pass the value which is the current value plus one so we're going to increase it and we're going to use dependencies on change and value here great now let's write the function on reduce so const on reduce is equal to use callback and don't forget uh we have to add the dependency array as well here and first let's check if the value is its minimal required amount which is number one so we don't want our counters to be able to go to minus or negative numbers so if value is equal to one we're just gonna return and break the function and otherwise we're gonna send on change and the value we're going to send is value minus one like that so for that we have to use value and on change like that great and now we can start styling uh our counter right here so go into this divs right here and let's give this a class name of flex Flex slash row items Dash Center justify Dash between like that great and inside of that create another div with the class name of flex Flex Dash hole like that and inside of that create a div with a class name of pump Dash medium and give it a value inside of title like that and now let's go back into rent model and let's give this counter a title right so for now I'm just gonna write um number of guests for example and you can see how it rendered uh here back great now let's go back into components inputs counter and let's continue working on this so uh just below this div which holds our title let's add a div which is going to hold our subtitle so let's give it the class name of font light and text Gray Dash 600. and inside of that we're gonna pass our subtitle and if you want to you can go back into rent model and just give this a subtitle of how many guests like that and you can see how that is rendered here as well we're going to change this we're going to use some different title and subtitle but just so you can see the placeholder now all right and now outside of these two divs uh create another div right here and with the class name of flex Flex Dash row items Center and gap-4 all right and inside of that we're going to create another div like this uh we're just going to have an on click on on reduce so this is going to be our minus button and let's give it a class name so class name here is going to be w-10 h-10 rounded Dash full border Dash square brackets 1 pixel border Dash neutral Dash 400 Flex items Dash Center justify Dash Center text Dash neutral Dash 600 cursor Dash pointer cover opacity minus 80 and transition like that and you can see how we now have a nice little button here and the icon we're going to use is AI outline minus so we're going to write a i outline minus between import from react Dash icons AI right here at the top so react Dash icons slash AI like that uh and you can save this and see how we have a little minus icon right here great now just below this div we're actually going to create a space for the current value so for that we're going to write last name font light text Excel and tax Dash neutral Dash 600 like that and inside of that we're going to pass value great uh and now we're gonna have our plus button here and for that you can actually copy this div right here and you can just paste it below that and now you can see we have the two options right here and instead of AI outline minus we're going to write AI outline Plus which you can import from react Dash icons AI so we have ai outline minus n a outline plus right here so these class names are exactly the same as they are for this one and except on reduce we're going to add on ADD like that great now let's go back into our rent model here and let's change uh this to just guests like that and this is going to be how many guests do you allow and it's going to be a question like that so it looks a little bit uh better like that great and now I'm going to give it a value of guest account which right now does not exist so let's go right here where we Define category and location and write const gas count watch guest count like that and you can see that we have guess count and the default is one and you can go ahead and save and you can see how it translated right here great now let's go uh here to the counter and let's add the on change let's extract the value and let's write set custom value and let's give it an ID of guest account and just pass the value here and I can click save and now if you try and click plus you can see how it increases if you click minus you cannot go below one great amazing job and now we're gonna copy this counter a couple of times like this and between them we're gonna add an HR line like this and another one here so it looks a little bit uh better great for the second one we're gonna add the title rooms and we're gonna write how many uh rooms do you have [Music] and for the last one we're gonna write bathrooms like this and we're gonna ask uh how many bathrooms do you have like that great and now that we have guest rooms and bathrooms we have to change all of these custom values and values right here so let's go here to the top and let's create uh let's copy this two times and for this one we're gonna write room count like this and this room count exists uh right here we defined it and last one we're gonna write bathroom count like this so now we have guest count room count and bathroom count great and let's go here to the bottom and let's replace these rooms so we're not going to use gas count for the rooms we're going to use room count and instead of set custom value guest count we're going to write Rune count books like that and for the bathrooms we're going to change this to the bathroom down and we're going to change the value to bathrooms sorry bathroom account like that great and now you can try and you can see how individually you can control every one of them great amazing job you'll finish the counters uh what we will do next is uh the images using cloudinary I'm very excited to do that with you great job let's continue and let's create our step for uploading the image so that will be the next step after these counters so let's go and let's find our if clause for info and just below that let's write if step is equal to steps dot images like that and then let's change the body content once again let's write a div inside and let's give it a class name of Plex Flex Dash call gap-8 again and let's give it a heading with a title of add a photo of your place and a subtitle of show yes what your place looks like exclamation point great and now if you click next you can see that we have the image step right here and we're going to create a component for image uploads great now let's write image upload like this at the bottom and let's save and of course we're gonna get an error so let's go into components inputs right here and create a new file image upload.tsx right here and let's mark this as us client and let's just write image upload and fix the error now we can go back into rent model right here and import this from dot dot slash input slash image upload great uh and now that the error is gone let me just find where we are okay I'm gonna keep my tab opened so step four that is great uh and let's go into image upload and in order to start working on this first we're going to need to create uh our cloudinery account so go into Google and type in cloudinary and select a first link and we're going to click sign up for free great I'm going to use a sign up with Google agreed once you're in um you can just finish this short survey that they have for you great and once you're in you can click on the dashboard right here and you can see your Cloud name right here amazing so that's it you have a free cloudinary account no credit card needed and you can see how much you've used of it right here great now to continue we're going to use the next cloudinary package right here so I'm going to zoom in a bit so you can see what we have to install and what we have to add so let's go ahead and let's shut down the application and let's write npm install next Dash cloudinary great after this has been installed you can actually go ahead and npm run Dev your application again and as you can see we have to add some environment variables here so let's go and I'm gonna close everything for now and I'm gonna go into my DOT environment file right here and the uh field that we need is next underscore public underscore cloudinary underscore Cloud underscore name so let me just confirm that I copied this correctly yes okay so the next underscore public underscore cloudinary uh underscore Cloud underscore name and the value you're gonna paste in here uh is going to be your Cloud name right here so go ahead and copy that and just paste it here like that all right and now you can go back into uh app components inputs image upload right here and let's actually start coding this so we're going to import clb upload widget from next Dash Cloud generic we're also going to import image from next slash image and we're going to import use pullback from react and we're going to import DB photo plus from react Dash icons slash TV great now let's uh declare some Global variables for cloudinery so declare Global bar cloudinary like that great and now let's write the interface for image upload props so interface image image upload props is going to have on change which has a value which is a type of string and just returns a void and the value is a type of string so these are going to be URLs all right and now let's assign these props here so react FC image upload props and let's extract on change and value like that great and now let's write the function for a successful upload so const handle upload is going to be a used callback which accepts a result which is the type of any and just open the arrow function here don't forget the dependency array and all we're going to do is call on change from the props right here so call on change and we're going to pass in result dot info dot secure underscore URL like this great and make sure you pass the on change in the dependency array like that all right and now let's actually write this return function here so what we're going to write in here is CLD upload widget like that it's a self-closing tag and inside of that we're going to assign on upload so on upload is going to call this function handle upload which we just defined upload press it can be empty for now we're going to fill that later and options is going to be an object with Max files one all right and now it's actually not a self-closing tab so go ahead and close it like this CLD upload widget like that and inside of that we're gonna open uh curly brackets we're going to open a function like this and we're gonna distract her the structure open from it like that and then you can return an arrow function just like this great and inside of that we're going to call another return like this great and that return is going to be a div which on click is going to call an arrow function which is gonna called open question mark and execute it like this because there is a chance that this open does not exist by the time you open the model so just ensure that it exists with this not make sure that this exists but just ensure that you don't get an error accidentally you could technically just do this but it happened to me that I got an error so just to save yourself you can write this all right and let's give it some class names so class names are going to be the following it's going to be a relative cursor Dash pointer power opacity Dash 70 transition border dashed border two p 20. border Dash neutral Dash 300 Flex Plex Dash call justify Dash Center item slash Center gap-4 text Dash neutral Dash 600 like that and inside of this div let's use the TV photo Plus like this and I'm just going to refresh my local post because I shut down the application so I have to restart it like that um all right let's just wait a moment for this to be refreshed so we can see the most up-to-date uh code all right so I'm gonna open uh the Airbnb or home again go to step two step three and step four and you can see uh that I have my little area here which I can click and I'm just going to give a size to this DB photo of Clips D like this so it's a bit larger great that's exactly what I wanted uh and just below that I'm gonna give a div with a click to upload a text and a class name of font semi bold text LG like that great um and just below that we're gonna write a conditional which is going to call Value and end and Open brackets like this and it's going to return a div inside and that Dave is going to have a last name of absolute inset-0 W full and H dash full like that and inside of that div we're going to render an image and we have already imported this image from next slash image right here let's give it an out of upload uh let's give it a fill option a style option with an object inside that says object fit cover like that source is going to be value and that should be it all right and now you can go ahead and try and click on this and you can see that it opens this uh cloudinary widget but if you try and upload something so I'm going to go into my desktop and I have some pictures here so I'm going to try and upload this and you can see that it says upload preset must be specified when when using unsigned upload right here so uh it did not actually succeed with uploading this so you're not seeing anything here so in order to fix that um all we have to do is we have to assign and upload preset which we left empty right here so I want you to go into cloudinary again and you need to create a upload preset so go into your settings in the lower bottom corner and click on upload right here and I have a couple of this because I was testing around but don't worry you probably won't have anything except ml default here so just find add upload preset function right here and click on it and very important signing mode must be unsigned like that and you can just copy this right here and click save if you have not copied it you can just copy it from here so your newest one is going to be at the top right here and just go into your upload present and paste it right here great so if you try now just make sure you refresh before you do this so it's up to date after we added this upload preset right here all right and let's try now I'm gonna try and upload something okay and I get an error because we have not passed an on change function but if you go into cloudinery and go into Media Library you can see that you have some of the images that you uploaded great and these are some of the default images that claudinary has but you don't have to focus on that so this is the image that I uploaded and it's right here great so just to recap here on the dashboard you can find your Cloud name which you will use in your uh environment variable right here but if you go into settings right here on the bottom and click on upload right here in the sidebar you can find your upload presets and make sure when you create an upload present you can do it again make sure you select unsigned here that's very important and click save and then just use this present name right here in the prop upload preset like that and everything should be just fine okay now let's go back into our models into register model register model not register model sorry rent model so let's go back here and let's add some values here so I'm going to add a value which is image SRC and I'm going to add on change which is going to call a value and pass it to set custom value give it an ID of image SRC and value like that now we need to Define this image SRC value right here and for that let's just use const image SRC is equal to watch image SRC like that I'm going to refresh everything now and we're going to try again great so I'm going to click Airbnb my home I'm going to go to the fourth step I'm going to click on upload and I'm going to upload an image here and you can see we get another error you're probably familiar with this so it's the same error we got when we created social login it says that next image does not re uh know the host name rest.cloudinary.com so you can just copy this uh hostname right here and go into your next.config.js and just paste it right here in domains and after you do that you have to shut down your application and run it again so make sure you refresh your localhost after that so I'm gonna try now I'm gonna click on Airbnb or home go to the fourth step and try and upload an image great you can see it's uploaded and transferred as a value right here and if I want to replace it I can just click on it again and let's say I want to upload this image now and after a couple of seconds it's going to be replaced with the new image as well and I can actually go back and I can go forward and saved in our form right here in the rent model in image source value amazing amazing job you successfully set up cloudinery so let's just recap what we need for cloudinary one more time make sure in your environment variable you have the next public cloudinery Cloud name which you can get from the dashboard right here in cloudinery so make sure you select dashboard and you can copy the cloud name right here and for the upload present make sure you go into settings right here at the lower left corner and click on upload and right here upload presets you can create as many of them as you want just make sure it's unsigned so one more time if you're creating this for the first time make sure you set this to be unsigned like that great great job we just have a couple of more steps to do and we will successfully create our first listing so let's wrap up our rent model right here we just have a couple of fields left so I'm gonna go back into app components models and I'm gonna select rent models right here and last time we finished the images Step of this model so in this part we're going to create the last two steps which are description and price which are going to be very simple because we already have all the components for that so we're not gonna need to create anything new so let's go ahead and let's write if step is equal to steps dot description in that case we're going to modify the body contents to a div the class name of flex Flex Dash core gap-8 and we're gonna give it of course a heading with a title of um how would you describe your place question mark and a subtitle of short and sweet works best exclamation point like that and now if I go next here next here next here and next here you can see that I have this how would you describe your place step right here great and now just below this heading I'm gonna add an input which we already have but we're gonna have to import it so just make sure you import input from dot dot slash input slash input we already have this input component because we used it in our register model and if you remember we can now pass register and errors here great so all we need to register this component no need for these custom values or sending a value like that all we have to do is the following we have to give it an ID of title we have to give it a label of title we have to give it a disable crop which is going to be controlled by is loading which we don't have for now but we're gonna create it we're gonna give it a register which we have from use form and we're going to give it errors from errors which we already have in use form and we're going to pass in required and let's just fix this is loading error right here so I'm just going to create a state uh right here at the beginning so I'm going to write const this loading set is loading you state and default is going to be false like that so just make sure you add this your state is loading here and then uh you can pass it in this new input right here great I'm going to go ahead and open this again and great we have our title right here and just below that I'm going to add an HR tag so we have a little divider here and I'm going to copy this input and I'm gonna paste it here and the second one is going to have an ID of description description and the label order description as well uh and that's all we need for that like that exactly great uh and that's it for our description step right here and we're going to create the last step which is price so if step is equal to steps dot price right this year body content is again gonna have a div with a class name of flex Flex call gap-8 and inside we're gonna add another heading with a title of Now set your price and the subtitle of how much do you charge per night like that great and if I go and click next you can see we have the last step right here and now we're going to use this input component again here and if you remember our input component has a special prop uh called format price which we added right here so now we're gonna see if we did that correctly so let's go back into rent model here let's give this input an ID of price a label of price uh we're gonna add the prop format price to it so format price like this is equal to format price true like that so you can write either like that or you can just do this it's the same thing the type is going to be number disabled is also going to be controlled by is loading and register is going to be controlled by register Errors By errors and it's going to be required as well great so now as you can see we have our price and why do we have this pre-selected number one well that because that's what we added right here in um use form default values so we added price to be one by default great um and now what we have to do is we have to make a function of this handle submit right here so first thing I'm gonna do is I'm actually going to create our on submit function so let's go just below this on next and on back right here and just below that I'm going to create a function on submit so const on submit it's going to be a submit Handler function which you can import from react hook form right here great and it's going to accept field values which we already have imported and it's the same thing that use form uses right here and it's also imported from react hook form great and now what we're gonna do here is we're gonna open uh the beta props right here and we're gonna open a function like that and first thing we're going to check if we are on the last step so if we if the current step is not equal to steps dot price so if we are not on the last step in that case we're going to return on next like that so every time we click next we're actually going to call this on submit function so inside of here we're going to check whether we clicked next on the last uh step or if we clicked it on any of the other steps great so if it isn't the last step we're just gonna go forward like this so it's going to load the next thing but if we are on this price step and click create in that case what we're going to do is we're going to set is loading the true which we just created earlier right here so we have this uh is loading State like that and we're going to set this is loading to true and then we're going to call axios which you can import from axios right here so you can import access from axis like that great and we're gonna make a post call the slash API Slash listings and we're gonna pass in the data which we got right here in this parenthesis and the reason we got that data is because we're going to wrap this on submit uh if you remember like we did in login and register with this handle submit uh wrapper like that all right and after we make this access postcode let's add a then Arrow function in which we're going to trigger a toast which you can import from uh react hot toast like that all right so once we get that toast we're gonna write toast.success this thing created exclamation point like that and we're gonna call router which we don't have so let's go to the top right here where we use rent model and let's write const router is equal to use router and make sure you import it from next slash navigation so right use router like that and make sure that your router is imported from next slash navigation not next slash router very important next slash navigation all right and we're gonna call after toast that success we're going to call router that refresh like that great and then we're going to call reset which where do we get this reset from well also from this um use form so when we submit the uh when we submit our listing and if it's successful if it's managed to create it we want to reset the entire form all right and after this uh reset I just want to reset the steps as well so I'm going to write set step to be steps dot category so we're going to reset the step to um the first step and we're going to close the model so rank model that on close like that perfect and let's write a DOT catch function like this we're going to write those that error something like wrong like that all right and finally in this finally function all we're going to do is set is loading pupils like that great and now we have to use this on submit uh all the way in our model down here so let's go down to the model and if you remember we added that on submit triggers the on the next function but we're not going to do that so what we're actually going to do is we're going to write um handle submit on submit like that where do we get this handle submit from well we get it right here um from our use form the structure right right here and we are going to wrap our newly created on submit function here great and now if you actually try I'm going to select here I'm gonna go next I'm gonna select Croatia here I'm gonna go next uh I'm gonna write a couple of items right here I'm gonna click next uh I'm gonna try uploading an image again just to confirm that that works as it should so let's just wait a moment for that to uh align okay we have an image and now I'm gonna try and click next here and you can see how it prevents us because these are very simple Fields inputs which we are able to control with errors the previous fields are not able to be controlled by errors in react hook form but we can control this builds great so we have to add a title which is going to be test listing and we have to add our description which is going to be test description like that and the same thing is going to be for the price like this great and we get an error why well of course because this endpoint which we just uh pointed at uh Slash API Slash listings does not exist so let's go ahead and let's create that endpoint I'm going to close everything uh and I'm gonna go into app API and instead of register in this API folder I'm going to create a new folder called listings like that and inside this listings I'm going to create route.ps file so in our API folder you should now have the register folder with route.ds and the newly created listings folder with route.ds right here great now let's go ahead and let's import some stuff so let's import next response from next slash server let's import Prisma from add slash app slash blind slash Prisma DB and let's import get current user from add slash app slash actions slash get current user if you remember we already use this get current user in our layout.tsx and this is a great thing about server components and this way of defining actions right here so we can use this both in a component and in our API if needed great so let's continue by writing our post route so we're going to write export as synchronous function post which is going to accept a request which is a type of request like this let's open this object and let's write const current user is equal to awake get current user like that we're gonna check if there is no current user and in that case we're going to return a next response dot error like that great now let's extract our body so const const body is equal a weight request.json like that and now let's extract all of the fields which we will have from our body that is title description image source category room count bathroom count guest count ion and price and you can extract all of that from body right here now I'm gonna iterate uh over all of these items and we're gonna check whether one of this is missing because all of them are required and that's when we're gonna throw one error so go ahead and write object dot keys body for each value which is a type of any and just open an object right here sorry open a function right here and we're just going to check if question mark body value so if any part is missing inside we're just gonna throw next response that error like that so I made a mistake here and this iteration and throwing off next response doesn't actually do anything if you want to you can keep it in your code or you can completely remove it from your code great to be um and what we're going to do now uh is we're actually going to create this listing so go ahead and write const listing is equal to away prisma.listing dot create and inside of that we're going to open the data object and I'm going to write title description image source category room count bathroom count yes count location value is going to be location dot value like that price is going to be parse int price 10 like this because uh I want to ensure that our price is an integer like that and user ID is going to be current user.id like that great and all I'm gonna do is return next response the Json listing like that great and that should be it for our post route right here let's go ahead and let's try this again so I'm going to collapse this a little bit so we can see our Network tab right here and just for the sake of making sure that everything is right I'm gonna refresh everything and I'm gonna create my listing again from zero all right so I'm gonna click Airbnb or home I'm gonna select the category of Beach I'm gonna select uh Croatia from my location I'm gonna select three guests three rooms and three bathrooms I'm gonna upload a photo uh all right let's choose something like this great I'm gonna click on next I'm gonna set the title to Villa in Croatia and description my description like that I'm gonna click next and the price is gonna be let's say 500 per night uh and now let's see once we click create you can see that we are able to point right here and just like that we get listing created success message here and if you actually check the preview you can see that we have created uh our listing you can see how that location transferred to just location value uh room count price 500 everything seems to be completely fine and you can actually go into your mongodb and let's refresh right here in listings and you can see we have our first listing right here along with the image source along with the category with the room count bathroom count gas count price location value user ID title and description amazing amazing job in the next part we're actually going to load that listing right here amazing job you successfully finished the entire uh model great and you can go ahead and you can try uh and just not fill some data so I'm Gonna Fill the required ones I'm going to click create and you can see how we get something went wrong because we actually threw an error great amazing job what we are going to do next is we're going to load this newly created listing in our database to display right here on the index page and to begin doing that I'm gonna close everything and I'm gonna go back into app layout.dsx right here and we're going to wrap these children in a div which is going to move it just below these items here so let's go ahead and write a div here as in the right class name pb-20 pt-28 like that great we still can't see anything that is because we have these categories so let's go into our app page.tsx right here great and we have this function home right here and first thing I want to do is I'm going to remove this long div which we created at the beginning of this series so what we're going to do now is add our client only component from dot slash components slash client only and inside of that we're gonna add a container which we can import as well from dot slash components slash container here and then we're gonna write a div and we're going to give it a couple of class names right here so let's go ahead and write pt-24 grid grid Dash whole slash one SM grid Dash calls Dash 2 and the grid Dash calls Dash three LG grid Dash calls Dash 4 XL grid Dash calls Dash 5 and 2XL grid Dash calls dash six like that and we're going to give it a gap of eight great and we're gonna write a nib here for now which is just gonna say uh my future listings like that so this is where we're gonna load uh our listings and if you refresh you can see that we have our space for my future listings right here uh and what I'm going to want to do now is I want to create an empty State here so let's say we select a bunch of these filters let's say we select the countryside and in the future we select some date range and the number of guests and we select some country and what happens if there are no listings available in our database that fit the criteria that user has selected well we have to present them with an empty state so for now what I'm going to do is I'm gonna add a constant here is empty I'm gonna set it to true we're gonna replace that with something Dynamic later and what I'm going to do is I'm going to write if is empty like this and we're going to return something else it's also going to be wrapped in client only so it doesn't mess with hydration errors and wouldn't create a new component called empty State like this great uh and let's go into our components here and let's create a new file empty state.vsx let's name it empty state and just write the div that says empty like that and let's go back into app uh not in components but page.dsx right here and let's import this empty state from dot slash components slash empty States like that just wait for this to load okay and we cannot see anything at the moment so let's go back into components and our newly created empty state right here and first I want to create an interface for this so go ahead and write the interface empty save like this and inside I'm gonna write title which is an optional string I'm going to write subtitle which is also an optional string and I'm also going to write show reset which is an optional Boolean like that great now let's assign this props to this empty state so react FC empty State like that and let's just extract all of this so title and let's give it the default value of no exact matches like that and we're going to do the same for subtitles so we're going to extract subtitle and give it the default value of try changing changing or removing some of your filters like that all right and lastly let's extract show reset like that great now let's add our router so const router is able to use a router and I made a mistake here so not from slash router but from slash navigation and of course since we are using hooks inside this component let's just make sure that we also add use client at the top you can see how we got this warning use router only works in client components so let's go ahead and add use client here at the top of our empty state and once this refreshes the error should go away all right and now that we have our router from next slash navigation so make sure it's not from next slash router it needs to be from next slash navigation like that let's enter this div and let's give it some class names here so class name it's going to be whoops H dash open square brackets 60vh Plex backslash call gap-2 justify Dash Center and items Dash Center as well all right and instead of empty right here we're going to add our heading component which you can import from dot slash heading right here at the top because we already are in the components folder and let's give this a title of title which we have in our props and subtitle of subtitle which we also have in our props right here great and you can see how that looks right here great and one thing I want to do is I want to add a center prop that is heading which if you remember we never used so far but it's great that we have it because it fits this case exactly as we wanted great and just below that we're going to add another div the class name of w-48 and mt-4 and inside of that we're going to create a conditional that depends on show reset like that so give it a show research and end and open this and we're going to render a button which you can import again from dot slash button all right I'm going to close this and I'm going to give it some properties so it's going to be outline and it's going to have a label of remove all filters like that and on click what it's going to do is it's going to call the router and push to an empty slash so it resets all of the filters great so now we have finished our empty State component right here now let's go back into app page.dsx right here and I want to add a prop please show reset like that so now when we refresh this you can see that we have a button uh remove all filters and I want to demonstrate you so I have selected Countryside let's say I select Lake when I click remove all filters that gets removed perfect great uh what I want to do now is I want to create uh an action like we did with current user since home is a server component we can call the database directly and we won't need uh to create an API call for that so let's go into a actions right here and create that listings.ts like that great and I wanna export default async function get listings like this and all we will do for now is open a try and catch block right here and let's handle the error first so catch error any all we're gonna do is throw new error error like that great um and what we do here is we're going to import Prisma first so let's go to the top right here and import Prisma from add slash lip slash Prisma DB like that uh my bad from add slash app slash lips slash Prisma DB like that a great and now all we're gonna write is we're gonna fetch all of our listings so go ahead and write const listings is equal to away prisma.listing dot Point many and all we're going to do is order by created at descending like that and next thing we're going to do is just return listings like that great and that's it that's all we need to fetch our listings that's completely amazing we have no need to create an API out what we can do now is go into page right here and we can import that function get listings that we just created so I'm going to go here and I'm gonna write const listings is equal to a weight get listings from dot slash actions slash get listings right here at the top of course we cannot use a weight inside of a non-asynchronous function so just make sure to mark this as a synchronous like that and now listings are working great and instead of is empty here what I'm going to use is if listings got length is equal to zero like that great uh and now I'm going to refresh this page and you can see that we no longer show the empty State and that is because our listings are not empty we have one listing which we created in the previous part of the tutorial so what you can do now is really really cool you can already go ahead uh and write listings.map listing and for now let's just write any like this uh and you can return a div which is just going to say listing that title for example let's just save this and you can see we have our title uh Villa in Croatia which I gave it right now so we are successfully loading listings using our server component no need for an API call amazing and now instead of using uh this empty div let's actually use a proper component which we're going to create and it's going to be called a listing card like that so we're going to create this new component and inside of that uh first thing we're going to give it is a key of listing.id and then we're going to give you the data of listing uh just like that perfect now we're going to get an error because this does not exist so let's go ahead and let's go into app components and create a new folder called listings and inside of that folder create a new file uh listingcard.psx like that I'm just gonna name it listing card and it's going to return a EMT div listing card like that and now we can go back into page.tsx right here and just make sure you import the newly created listing card right here uh so import listing card from dot slash components Slash listings slash listing card and save the file great let's wait for this to load great and we're gonna pass a couple of more props to this listing card because this listing card is going to be uh used both for this initial uh index but we're also going to use it in our favorites in our reservations properties and trips so uh it's gonna accept a different kind of props alongside this data it's also going to be able to show some buttons if needed so let's go ahead and before we begin working on that let's also fetch our current user right here below this listings and we already did that in navbar so we can feel free to do it again in the same way here so cons current user is equal to await get current user which you can fetch from dot slash actions slash get current user like that great it doesn't matter if this current user does not exist so we're not going to get an error because if you remember in this function we purposely don't throw any errors uh because we don't want anything to break if there is no error here if there is no current user because this page is available for send out users as well all right and now what I'm gonna add this listing card is the current e-user as well great great job and now let's go back into components listings listing card right here and let's go ahead and let's write the interface for this so I'm gonna go right here interface listing part props and now you're gonna see what kind of props we're gonna accept here so first of all data which is a type of listing from ad slash at Prisma slash client like that then we have a reservation which is optional so for example we did not use it now but we're going to use it in the future and that's going to be a type of reservation as well from at Prisma slash client like that then we're gonna have a prop on action which is optional and it's going to accept an ID of type of string and it's going to return a void like that disabled is going to be an optional Boolean and we're gonna have action label which is an optional string and we're going to have action ID which is uh an optional uh string as well and current user which now uh we could also remember uh we sanitize our user so we could technically write a user from Prisma client but if you remember we are sending some non-sanitized data like date inside client components we cannot do that so instead of using user I'm going to import save user which we created the types for and we are also later going to create safe listing and safe reservation as well but that's that's for a later job to do uh great and we're also going to add a pipe here now so we fix any uh server errors all right I'm just going to refresh this right here great and now we can assign this props right here so go ahead and write react.fc listing card props and just extract all of these values here so data reservation on action disabled action label action ID and current user just like that all right and now inside of that first let's get our router which we will also need for this const router is equal to use router which you can import from next slash navigation and since we are using router also make sure that you go to the top and import this listing card as use client so go ahead and write use client like this perfect and now we can use router inside of that if you didn't find use client you're probably going to get an error for using uh use router here all right and below that we're also going to need to use our hook use countries so go ahead and write cons and we're going to extract get by value from views countries like that and you can import use countries from ad slash app slash hooks Slash use countries just like that and let's just order this so it's nice and neat like that all right and now that we have these two hooks first let's actually get our full location because if you go into our database you can see that the only thing we store regarding location is location value so we don't store latitude and longitude we don't store the flag we don't store the region we don't store the name of the country we only store location value and that is the safe space on the database so we don't have to work with objects there if if there's no need to so what we can do here is write const location is equal to get by value and inside of that we're going to pass data dot location value like that beautiful all right and uh now I'm gonna write a function called handle cancel which we will use in trips reservations uh and other types of uh listing card modifications that this component will have depending on where we are importing it so I'm gonna write const uh handle cancel which is a use callback like that and inside of that is going to accept an event which is a type of react. mouse event which accepts HTML button element like that and inside of that we're gonna use this to stop propagation so e dot stop propagation like that and what we're gonna do next is we're gonna check if the current card is disabled so if disabled in that case uh we're going to just break and return the function otherwise we're going to call on action question mark and execute it like this because on action could be undefined and just make sure that you add the dependency array right here so just after this you can add a dependency array which is going to use on action like this and let's see what are the errors here so I made a mistake HTML button element like this uh and on action of course you need to accept the action ID grade and we also need to use that action ID just like that and let's see what's the error of the action ID okay so we can fix this easily by just assigning an empty value the default of action ID just like that and let's see what is the errors here so we are missing another prop disabled great and I'm just going to collapse this like this okay great so now we have our handle cancel function which stops the propagation uh returns or breaks the function if the listing card is disabled and then close the on action if it exists using this question mark Dot and execution right here and passes the action ID and we fix the type error by giving it a default value of an empty string right here in props great so we have our handle cancel function right here so next thing we're going to write is a constant for our prices so go ahead and write cons price is equal to use memo which you can import from react the same way we did with use callback right here all right and this use memo is going to have a dependency array so don't forget to add that right here and let's just write if reservation so if we have a reservation in that case we're going to use the reservation price which is going to be return reservation that total price like that otherwise plug in the return data dot price and don't forget the dependency array in which we will use reservation and data dot price great great job and now I'm going to create a constant for our reservation date so if we are using this listing in reservation we want to display from N2 or start and end the date and we're going to use that using this constant so const reservation data is equal to use memo as well don't forget the dependency array at the bottom and we're going to write if there is no reservation the reservation date is going to be null so make sure you return null like that otherwise we're gonna Define our start and end variables so constart is equal new date reservation dot start date and end is going to be new date reservation dot end date like that perfect uh and what we're going to return uh is a date FNS function so let's go into our terminal to install that package so I'm just going to close this right here and write npm install date Dash FNS like that let's wait a moment for this to install great and now we can go ahead and npm run Dev your project again and when you run npm run Dev make sure that you refresh your localhost so everything you see is up to date great and in order to continue let's just go to the top right here and I'm going to import format from date Dash FNS like that so make sure you import uh format from the FNS which we're going to use here in the reservation date so what we're going to do is we're going to return an open template literal strings because we're going to use some objects here like this so open that and write format and inside of that you start and the format we're going to use is PP which creates the look of the data that we want and just outside of these curly brackets right here I want to uh to give a space give a dash and give another space and then open this object again and we're going to write format and in the exact same way so we're gonna get say something like format uh we're gonna say something like reservation data is uh from March 3rd 2023 till March 5th 2023 and now let's fill this dependency array with reservation like that that is the only thing we need inside perfect and now we can go on to styling our div all right here so I'm just going to remove the inner content and I'm going to start giving this some class names right here so we're going to say um pull Dash span-1 cursor Dash pointer and group like that and this main div is also going to have on click which is going to be a narrow function and what it's going to do is push so rather the push and open template literal strings inside and write Slash listings slash open this special object and write data.id so when we click on this listing card we're going to redirect it to a single view of that listing which we will create now uh sorry which we will create later this is for now a 404 page great and now inside of that let's create another div and we're going to give it a last name uh of Plex Flex slash call Gap Dash 2 and wool like that and inside of that we're going to create another div with the following class names so inside we're going to write aspect Dash Square W Dash full relative overflow Dash hidden and around that Dash Excel like that and inside of that we're going to use the next image component so we're going to write image like this and you can import image from next slash image right here at the top next slash image oh great and let's give this uh an ALT of listing let's give this a source of data dot image SRC and a class name is gonna have object Dash cover H dash full W Dash full group our scale 110 and Trend position like death beautiful so let's wait and refresh the page to see uh if anything is visible uh all right so we have a an error here let's see what that is for okay so we are missing one prop from this image what we have to do is add fill as well great and let's refresh now to see our image source in action and basically what group Dash hover scale 110 means is that when we hover on this group div right here so when we hover on any part of our listing card we're going to enlarge this image right here and just like that you can see our image loaded and you can see how when I hover it has a very cool zoom in effect beautiful all right uh what we're gonna do next is we're gonna add a heart button right here at the top so in order to do that let's just go below our image here and let's write a div and let's give it a class name of absolute top Dash three and write Dash three and inside of that we're gonna use a new component called heart button so go ahead and write hard button like this and just give it some props so it's going to have a listing ID which we will get from data.id and it's going to have current user from current user uh like that great uh and now we're gonna get an error because this hard button does not exist so let's go um into components and let's just uh create a new component right here art button the PSX like that so hard button it's just gonna return a bit for now great now let's go back into components listings listing card and let's import heart button from dot dot slash heart button and you can save the file and the error should go away and now we can go back into hard button and we can start doing some work here so first things first let's define this as use client component at the top and let's write the interface so interface part button rocks it's gonna have a listing ID which is a type of string and it's going to have a current user which is optional and it's going to use Save user from dot dot slash types uh pipe null just like this great and now let's assign this props here so react FC Parts button props extract the functions here so listing ID which is a type of string uh my bad suggest listing ID and current user like that nice uh all right and now we're gonna have to create a uh well actually we're not going to create anything for now uh we will later create a hook which will control this hard button uh but what will what we will do for now is we're going to hard code this so we're gonna write const has favorited people's and cons toggle favorite it's just going to be an empty function like this and we're going to modify this later to actually do some job inside great now let's go inside this div right here and let's give it an on click on click is going to be toggle favorite which you can already guess later this is going to be an actual function when we create an API for that and now let's just write the class name which is relative power opacity Dash 80. transition and cursor Dash pointer all right and now what we're going to write inside of that is AI outline heart which you can import from react Dash icons slash AI right here at the top and this is a cell closing tab and let's give it a size of 28 and let's give it a class name of pill Dash White absolute minus top minus open square brackets to pixel and minus right minus open square brackets again two pixels again and if you save the file uh it should actually show up here yep so it's right here I accidentally clicked on the listing so when I go back into my index page which will show all of the listings you can see a nice little white outlined heart right here at the top great and what I want to do next is create a AI fill heart component just below that which you can also import from react Dash icons slash AI inside it's also a safe closing tab it's gonna have a size of 24 and a class name is going to be dynamic so we're going to write has favorited which of course for now is false by default but later it's going to be dynamic and so if we have favorites this listing the color we're going to give it is fill Dash rows-500 otherwise it's going to be fill Dash neutral Dash 500 70 like this great so you can see how now it's a grayish color because we used an opacity on this neutral 500 color right here so just make sure that it has favorited we use fill Rose 500 otherwise we use fill bash neutral Dash 500 slash 70 like that and if you want to play around you can just modify this has favorited to true for example and you will see how it changes to a nice red color so let's just change this to false for now great all right uh and and now let's go back into listing card because we finished this hard button component so we can go back into listings listing card right here uh and let's continue our work uh so outside this uh heart button and outside of this div aspect Square which handles the image uh create a new div which is going to have a class name of font class semi bold text LG and we're gonna pass it a location question mark dot region and we're going to add a comma we're gonna open an object again and write location question mark dot label like that so let's just wait a moment for this to refresh and you can see how it says Europe Croatia right here nice and below that we're going to create another div with a class name of font light that's Dash neutral Dash 500 what this is going to do is it's going to render our reservation date if it exists otherwise it's going to show the category of this listing so go ahead and write reservation date pipe pipe data dot category like this so in our case we're going to show Beach we're going to show the category because our reservation date does not exist because we return the null if there is no reservation passed and if you remember we are not working with reservations yet so we just prepared this for the future uh when we need it all right uh and uh just below that let's create another the class name Lex Flex Dash row items Dash Center and GAP dash one like that and inside of that create another div with a last name of a font semi bold and just write a dollar sign space and give it a price we created this price constant right here so we either use reservation price or we use the listing price from the data like that great and you can save that so you can see how that looks and adjust below this div inside below this price div we're going to create another div which sorry we're not going to create a div we're going to create a conditional here so just below price they create a question mark sorry exclamation point reservation and end and open parenthesis here and we're going to write in div give it a class name of font dash light and just write night with great great job uh only the only thing that's left is just outside of this div which holds our price right here uh to create uh another conditional on action and an action label so I'm just going to Center this so you can see like this and then action label and end and open another parenthesis and we're gonna just write a button here which you can import uh from dot dot slash button right here the same way with heart button great and it is a self-closing tab so what I'm going to write inside is I'm going to give it a disabled prop which is going to be controlled by uh whether the general card is disabled it's going to be a small button and label is going to be action label and on click it's just going to be handle cancel like that great and we're not going to see anything now because again we're not working with reservations but I'm gonna remind you of this when we start working with reservations uh beautiful and what you can do now is try and create another listing to see how it looks so I'm just going to click Airbnb or home and I'm going to choose a Countryside listing for this and let's say we want to place it in Sweden like this I'm gonna add a random number of guests and bathrooms and I'm gonna upload an image that looks like a Countryside for example so let's go ahead and let's find something here uh I think this looks nice yeah this looks great all right so let's wait a second for this to upload great and once it's uploaded let's just wait a moment uh until it's appended here great and I'm gonna click next I'm gonna give you the title of villa in Sweden Countryside and a description of my description and let's click next and let's give it I don't know a thousand dollars per night it's very expensive and just like that you can see how it immediately updated because of our server component and it's right here amazing amazing job in the next part of the tutorial we're gonna enable favoriting of our listing right here and we're gonna create an individual view for each of these listings great job let's continue and let's create the routes for our favorite system right here so I'm gonna go ahead and close everything here and let's go into app API and let's create a new folder called favorites great and inside of that let's create a new folder called square brackets and inside of that write listing ID with a capital I so I accidentally deleted it so just like this listing ID great and inside of that folder you're going to create a new file a route.ts so the structure is API favorites slash listing ID and inside of that route.ds great so let's import next response from next Dash server like that and let's import get current user Chrome add slash app slash action slash get current user and let's import Prisma uh from App slash app slash lib slash Charisma VB like that great now let's write the interface for our parameters so interface I parents is going to be listing ID question mark string like this great and now let's export nipple asynchronous function host so I made a mistake here and instead of export default it should just be export a synchronous function post I'm going to catch this error later in the video and fix it or if you want to you can fix it now the request is going to be a type of request like that and we're also going to extract parents and a type of Paramus is going to be eye patterns like this great now we can go ahead and open this and first let's get our current user so cons current user is equal to await get current user just like that now let's check if there is a current user so if there isn't a current user in that case we're going to return uh next next response that error like that nice now let's extract the listing ID from our parameters so const listing ID is equal to parents just like that great now let's check the type of this listing ID so if there is no listing ID or if type of listing ID is not equal to string in that case we can throw new error invalid ID just like that great now let's create our favorite ideas array so we're going to write let favorite IDs is equal to an array in which we're going to spread current uh sorry we're going to spread open parenthesis current user dot favorite IDs Pi pipe Square empty array just like that great and now below that we're going to push the new listing ID inside so we're going to write favorite ids.push listing ID from this parameters that we got right here great and now we're gonna update user so we're going to write const user is equal to 08 prisma.user.update where ID is equal to the current user.id and weight of is equal to favorite IDs like this so this is a shorthand of favorite IDs favorite IDs you can either write it like this or you can just write it like that great and what I want to do at the end is just return next response dot jsonuser like that great and why we are while we are in here uh what we can do is we can create a delete function as well so let's go ahead and let's write our delete function export synchronous function delete like this which also has a request and parameters as well so let's extract params and let's give this uh the very same interface so parents is equal to I runs just like that great and inside of that we're gonna do the very same thing we're going to get our current user so write cons current user is equal to a weight get current user like that if there is no current user we're going to return next response error like this great now we're going to check uh now we're gonna extract our listing ID from params like that and every if there is no listing ID or if typo listing ID is not string in that case we're just gonna throw new error uh invalid ID like that great and now let's just modify the favorite like this we're gonna write let favorite IDs again uh and inside we're gonna do the very same thing uh with it here so we're going to spread open parenthesis inside of that right current user.favorite ids.5 empty array just like that and now we're gonna write favorite IDs is equal to favorite ids.filter find the ID and we're going to remove the ID that matches the listing ID just like that great and all that's left is to update the user so go ahead and write const user is equal to 08 prisma.user.update and inside we're gonna find the user by ID using current euser.id and the data we're going to pass is our favorite ID is the same way we did above great and just return nextresponse dot jsonuser like that the same way we did right here great so now we have a post and delete functions for our favorites listing ID so what we can go and create next is a hook that is going to call this route so go into Hooks and create a new hook called use favorite dot DS like that all right and inside of that let's import axis from axis let's import use router from next slash navigation let's import use callback and use Memo from react and let's import post from react dashboard post like that perfect now let's also import save user from dot dot slash types or you can use the app abbreviation like this I'm going to switch to this great uh and also import use login models so import use login model you can import that from dot Slash use login model great now let's write the interface for this hook so interface I use favorite like that it's going to have a listing ID which is a type of string and the current user which is optional type of Saved user or null like that great now let's write our hook so go ahead and write const use favorite and you can destructure a listing ID and current user and just give it the props of I use current uh my bad I use favorite like that basically this one right here great now let's go ahead and open this hook right here and let's add our router here so router is when we use router which we have imported from next slash navigation great now let's import login model from use login model like that and let's write our has favorited constant here so const has favorite 10 is going to be a use memo like that don't forget to add the dependency array right here and first we're going to get the list so accounts list is equal to current user question mark dot favorite IDs pipe pipe empty array like that so we don't get an error if there is no current user or if they uh there is no favorite IDs array and all we want to do is return list that includes uh listing ID like that and let's fill the dependency array with current user uh and what we are missing we are missing listing AP so print user and listing ID just like that amazing amazing job great now let's write our function to toggle favorite on and off so I'm gonna go ahead and write const toggle favorite it's going to be a use callback and it's going to be an asynchronous function uh which uh is an arrow function like this and just don't forget to write the dependency array right here uh great and now let's go ahead and let's expand that like this and let's give this uh uh an events parameter which is a react without mouse event which handles HTML div elements just like that so this is the only parameter we're going to have and inside of this function first things first we're going to stop the propagation so e dot stop propagation like that then we're going to check if we have the current user so if there is no current user we're going to return very important to return this so write return login model dot unopen like that so if we are not signed in and we attempt to like something uh we will open the login model instead otherwise we're going to open a try and catch block we're going to write let request like this because we can change it depending on whether we are liking or not and we're going to write if as favorited so if we already have this listing like that we want to unlike it right so we're going to write a request is equal to an arrow function which we'll call axials.delete like this making the right uh my bad right template literals like this and inside right Splash API slash favorites slash and open this special object in which we will write a listing ID like that great uh and then in this else function you're gonna write request which is also an arrow function but this time it's going to call a post request so access that post and again open template literals and just write slash API slash favorites slash listing ID like that great and just below that we're gonna write await request like that and then you can write router Dot refresh and post dot success which in which you can just write success like this and in the patch block right here all we're gonna do is toast that error something went wrong like that and now let's fill this uh dependency array right here so I'm gonna add current user I'm going to add has favorited I'm going to add listing ID I'm going to add login model and I'm going to add the router like that beautiful and now we have to export these functions at the bottom so just go ahead and write return as favorited and toggle pair it like this and remember to export vehicle uh use favorite book like that great and now we can go back into components heart button right here and remember we've hard-coded these functions so this time what we're going to do is we're going to replace them so cons as favorited and toggle favorite from use favorite new hook that we created which you can import from dot dot slash hook Slash use favorite right here and inside of that just open an object and write the listing ID and current user just like that beautiful and if we set up everything correctly we should be able to like and unlike something great so I'm gonna go and refresh everything and I'm gonna open my sidebar right here so I can see the network tab let's just wait a moment for this to load all right uh okay so once this is loaded I'm gonna go ahead and try and favorite this Europe Sweden Countryside listing and something went wrong all right uh I'm gonna go ahead and see uh what we did wrong so the mistake we made is actually very simple I want you to go back into API favorites listing ID route.ds and you can see that I wrote export default async post that is not correct we just have to export async post like that so we export async function post and we export as in function delete there are no default exports in the new route handlers so I'm gonna go ahead and refresh and I'm gonna try this again to see if this fixed the error but I'm pretty sure it did suggest in a moment when the page compiles I'm gonna go ahead and click like and as you can see it successfully turned red amazing job and I can try and click this too I can try unlike this great you completely finished uh the liking system so just to recap what we created I'm gonna close everything so inside API we have a new folder called favorites and inside of that folder we have uh square brackets listing ID and inside of there we have route.ds inside of there we have export as synchronous function post which uses the favorite IDs and pushes the new listing ID inside of that array and in the same way we have our export asynchronous function delete which also uses the favorite IDs array and filters the selected ID out of that array beautiful so the bug we had is that I wrote export default which actually broke this route Handler into not having any Methods at all so just make sure when you're writing route handlers that you do not export default just write export like this beautiful great and next thing we did is we added this hook use favorite right here which if you watched my previous tutorials might be something you're familiar with already I often use this style of hook so this hook accepts listing ID and current user it checks the current user favorite like this and checks whether the listing ID is inside of there to know whether to display a hard button or an empty gray button and we also have a toggle favorite function which calls the either delete or post request depending on if we have already liked this item or not and then uses a router that refresh to update everything that is on the screen so that's why we are seeing this change after an API call amazing amazing job so before we move on I want to bring your attention to our terminal if you look closely you will see that we get a familiar warning here which says that only plain objects can be passed to client components from server components date objects are not supported the date object which we are talking about here is our created add field so let's take a look where this is happening if you go into your app folder and into your page right here you can see that we use listings await get listings here so this is a server component and we are passing data to a listing card which if you open it and look at the top is a client component so this is where that warning is happening so what we have to do is we have to go back into our get listings action so go into app actions right here get listings and instead of just returning listings like this I'm gonna return safe listings so what I'm going to write is const saved listings and I'm gonna iterate over the existing listing so I'm going to write listings.map and I'm gonna extract this one listing and I'm going to open a function and I'm going to return an immediate object here so I can spread the current listing like this and all I'm gonna do is I'm going to fix the created ad like this and I'm gonna turn it into listing dot created at dot to ISO string like that so it's not a date that we are passing to the client component and then I'm gonna write return save listings like this and in order to do that I also have to create a new type so let's go back into our app folder into types into index.ts and just above safe user let's write export type save listing and we're going to write omit and we're going to open uh this pointy brackets again in the first parameter I'm gonna write um listing and in the second one I'm gonna write created at like this and you can import this listing from at Prisma slash client right here at the top where we have our user so in our omit method right here we're gonna omit from listing and we're going to take the field created at and now we have to add an add sign like here open an object inside and we're going to replace this created app with a string just like that and now we can use this save listing so let's go into our our app let's go into page.psx and we no longer have to put any here because it's typed right here as you can see and now in this listing card you can go into a app into components listings listing card right here and instead of data being listing it's going to be safe listing which you can input from apps types where we already imported our safety user like that and now if you go back into page.dsx you can see there is no error happening because the data that we expect and the data that we are passing is matching great job and if you go ahead and look into our terminal you can see that even when we refresh there are no uh warnings happening so just wait a second for this to compile and you can see that those warnings mentioning that only plain objects can be sent to client components are completely gone because we sanitized our listing to pass a string in place of created ad which was previously a date object great so we have refreshed and there are no warnings in our console great and now we're gonna move on and we're gonna create an individual page for each of these listings so let's start by closing everything up and let's go into our app folder right here and we're going to create a new folder here called listings like that and inside of that folder we're going to create another folder open square brackets and write listing ID like that and inside of that folder you're going to create a page.tsx just like that and I'm gonna name it listing page like this and all I'm going to return for now is a div which is going to say uh my individual listing page like that and if you save and if you try and click on any of those you should no longer get a 404 error you should now see this my individual listing page with the URL Slash listings slash our listing ID if for any reason you're not getting that and you're getting a 404 just confirm that the URL is Slash listings and slash an ID and you can check that if you go into components listings listing card right here you can see that on the main div we redirect the Slash listings slash data ID so make sure there is no API in front of this because this is all client routing here great so I'm going to go back into uh I'm gonna collapse this into app listings folder listing ID folder page.psx right here and in order to load this individual listing using the ID that we have in the parameters uh we're gonna we're gonna have to create an action so let's go into our actions right here and create a new file called get listing by id.ds so again this is not a route this is a direct communication from server component to the database which again I think is really really cool so let's start by importing our Prisma so input Prisma from add slash app slash lib slash Prisma really like that let's write an interface I parents so it is going to accept a listing ID which is a type of string like that and let's export default asynchronous function get listing by ID like that and the parameters is going to be params I turns just like that you can open an object here and write a try and catch block so in this try block first I'm gonna extract the listing ID from the parents so listing ID irons like that then I'm gonna check uh actually I'm not gonna check anything I can use this listing ID directly because this is not uh an API route so let's go ahead and write const listening is equal to await prisma.listing that find unique where ID is listing ID like this and we're gonna add includes sorry include like this user true so we want to load the user so we can load the profile image and the name of the user who owns this listing great now we're just going to check if there isn't the listing by chance so if there is no listing we're just going to return null like this great and now technically what we could do is just return listing but you already know that we're gonna get some errors sorry not errors we're going to get warnings in our terminal because we are sending date objects so let's go ahead and sanitize this uh right here in this action so instead of returning listing we're going to spread listing in this object and first let's solve the created add field so we're gonna replace that with listing.created at that ISO string like that great and we're gonna add a comma and now we have to sanitize our user from the listing so go ahead and open the user object and spread listing that user inside and now we just have to fix the created ad field again so go ahead and write listing dot user dot created add that to ISO string like that below that let's fix the updated ad so listing.user.updated at to ISO string like that and adjust at the end we have to fix the email verified which is going to be listing.user dot email verified question mark because it might be null so make sure you put question mark dot to ISO string like that pipeline now so we fixed any type errors great so we can return that safely and let's just finish writing our catch block right here so we can attach an error which is a type of any and we're going to throw new error error just like that and we finished our action here great and now we can go back into app listings listing ID page.tsx right here and let's import that function here so I'm going to write cons listing is equal to await get listing by ID and we're going to get the parameters uh from our function right here so first things first let's make this asynchronous like this and inside of here let's just extract parents and let's give it a type of parents I parents which we will create right now so go ahead and write interface I parents which is going to have a listing ID which is an optional string so you might be a bit confused now uh why are we not using react uh sorry why are we not using next navigation use router here since this is in our URL right well don't forget this is a server component we cannot use hooks inside of here so we can only use actions like this which directly communicate with the database but thankfully we can still access the parameters in server components which in our case uh is the URL here so I can now pass um listing ID inside so I'm just going to write listing ID sorry you don't have to do it like this you can just pass parents like this great and what you can actually do is replace this with listing.title for example of course if listing is available but you can see that it is so it says Villa in Croatia which is the listing ID which I clicked right here great uh and now uh since we have this little error here saying that listing might be uh possibly null because of course if you look at the function there is a chance that there is no listing and we return null uh let's fix that by uh importing empty State here so I'm going to go ahead and write if there is no listing we're going to return and go ahead and wrap this in client only like this you can import client only from add slash app slash components slash client only and inside of this we're gonna write empty state which you can import from add slash app slash component slash empty State like that beautiful uh and now we're gonna write the actual uh client component for our individual listing so we're going to replace this div with client only as well in this main return function and instead of using uh this title we're gonna write listing to client like this which we are going to create in a moment and we're going to pass a listing like this and we're also going to pass the current user like this which means we also have to add our current user so let's go ahead and write const current user await get current user which you can import from add slash app actions get current user right here at the top great and of course if we save we're gonna get an error because listing client does not exist so let's go ahead and let's create a listing client component remember we no longer have to write our components inside components so what I'm going to do to make this simpler is I'm going to create that inside of this listing ID folder on the same level where our page.dsx lies on so I'm going to create click uh new new file here in this listing ND folder and we're going to name it listing client.psx just like that and I'm gonna write uh listing the client like this and we're just going to return an empty uh div inside we're just gonna say a listing client like that and let's go back into page.dsx here and let's import listing client from dot slash listing client because they are in the same folder after all and you can now click refresh and the error should go away great let's just wait a second that this compiles great and we see the listing client option right here so let's go back into our listing client component right here and let's write an interface so interface listing client props first we're gonna accept reservations which are optional and they're a type of reservation which you can import from at Prisma slash client and just add an array at the end because there will be an array of reservations right now we are not passing them and we will work with them a bit later in this tutorial all right after that we're going to pass our listing which is required and it is a type of safe listing which you can import from ad app slash types and we created this at the beginning of this tutorial so I mean this section of creating a listing a client and one thing about this safe listing so it's not entirely correct to be just safe listing because if you go and look into our actions get listing by ID we also include the user so the type is now different it's not just a safe listing so so go back into your listing your client and make sure you write safe listing and open an object and write user say user as well from add app slash types great and we also have the current user which is optional and it's a type of save user type null like this and let's just assign these props here so react.fc listing client uh my bad so listing client props like that and let's extract these values so for now all I'm going to extract is listing and current user like that beautiful and if you go into page.tsx you can see that our errors have gone away because our listing is matching our values great all right so now let's extract the category from our listing if you look into our database in the listing you can see that we do have a category stored and it's a string called beach in this case but our category object actually has an icon and some description so we're going to create a constant which will use the existing categories array from categories component to get that exact category so let's go ahead and write const category use memo don't forget the dependency array right here and what we're going to do is return categories which you can import from add slash app slash components slash now bar slash categories again I'm going to repeat this we already did this once but just in case uh you forgot so you can import and destructure lowercase categories from a component called categories in our navbar right here and you forgot if you forgot that that is well that is this array of categories so we are going to map over all of these categories and we're going to match the label so we can get the icon and the description all right let's go back into the listing client here and now that you have imported these categories here what we're going to do is use dot find inside and we're going to find this uh item like that and what we're going to do is item.label is equal to listing.category like that and in the dependency array don't forget to add um listing like that great actually you don't have to pass the whole listing you can just pass listing dot category because it is a string great and now let's go and let's actually style our content here so instead of this div it's going to start with a container which you can import from add slash app slash components slash container which I did right here all right uh inside of that I'm going to create a div and I'm going to the class name of Max W screen LG and mx-auto like that right and inside of that I'm gonna create another div and give it a class name of flex Flex hole Gap dash six great and now inside of here I'm gonna create a new component called listing head like this and I'm gonna pass a couple of props so I'm gonna give it a title of listing dot title like that I'm gonna give it an image a source of listing.image source I'm gonna pass location value which is listing dot location value I'm gonna pass an ID which is listing.id and I'm gonna pass current user which we can get from current user because that is one of the props in this listing client component right here and if you save of course as always we get an error because this component does not exist so we're gonna have to create that one so let's go ahead and let's go into our components listings and create a new file called listinghead.dsx great and I'm just going to go ahead and fix the error so I'm going to name it listing head and I'm just going to write an empty there saying listing pad like this and I'm going to go back into listings listing ID file right here listing client and I'm going to import listing head from add slash app components listing listing head right here and once I save the errors should go away and oh yeah we have another error something that I forgot to do here so we're getting an error because we're using this use memo hook right here and because we're trying to import something from a client component so it's very important that we mark this listing client right here so this component right here make sure you're inside of that and make sure you put use client at the top because this is a client component you can see how it alerts us that uh it's acting like a server component and we cannot dot into a client module from the server component great so just refresh so the error goes away and we can see our listing ahead which should just display listing head text inside and just like that we can see the listing head great now I'm going to go back into my components listings listing head right here and first thing let's write an interface here but before I do that make sure you mark this as you use client as well all right so interface listing had props we're gonna accept a title which is a string we're going to accept location value which is a string as well image source which is a string IB which is a string and current user which is optional type of save user which you can import from add slash app slash types and just swipe now right here great and now I'm gonna assign these props here so react.fc this thing had props and I'm gonna extract all of those values so title location value image source ID and current user just like that great and now let's go ahead and let's get our full location using this location value and we can do that using the use countries hook so go ahead and write const get by value wrong used countries which can import from ad slash app slash hooks use countries right here great and now just fight on location is equal get by value location value like that great and now instead of this div right here write a fragment and the first value we're going to write here is going to be our heading which can import uh from Doc dot slash heading because we are in the components folder great so I'm going to close this right here I'm going to give this a title of title I'm going to give this a subtitle uh of a template little strings so write this in an object and you can just write location question mark dot region like that and give it a comma location question mark dot label like that and I'm Gonna Save the file and see if we did this correctly and we did so it's translated to Villa in Croatia and my location region and my location label great and now just below this heading create another div right here uh and let's just give it a class name of the following classes so we're going to add W Dash ooh we're going to add H square brackets 60 VH overflow Dash hidden rounded XL and relative like that and inside of this clip we're actually going to load our image so for that we're going to use image which you can import from uh next slash image I'm gonna add that to the top right here all right and now that we have that let's actually give it an out of image let's give it a source of image source let's give it a prop fill let's give the class name of object dash cover W Dash full and ALT of oh we already added an old all right and let's save that and see what we get and great we have a beautiful picture right here and uh just below that we're gonna open another div right here and we're going to give it a class name of absolute top Dash 5 and write-5 and inside of that we're gonna create another we're going to create anything we are going to reuse our hard button component which you can import from dot dot slash hard button we created this already before for our listing card component and thankfully we can reuse it here again so all we have to pass is the listing ID which we have passed as ID prop in this component and current user which we have as current user already great so I'm gonna save this now and just in a few moments we should see the hard button right here and you can see that if I go uh to my index page right here and if I favorite something uh on this page so now it's red here and if I click on it so it loads the individual page you can see that my heart is also red here and if I try to unlike it from here you can see that it becomes grayed out and if I go back you can see that it's great out here and the same situation is for this as well great uh that is it for our listing head component right here so now we can go back into our listing client component right here in listings listing ID folder and just below this listing ahead let's create another div and give it a class name of the following classes grid grid Dash calls one MD grid Dash calls dash 7 and the gap-10 and mt6 like that great and the first component we are going to put inside here is a new component listing info like this and we're gonna pass it a couple of props here so first thing is going to be user which is listing dot user category which is category we're gonna pass description which is which is from listing.description we're gonna pass in Rune count which is listing dot Rune count we're gonna pass in guest count which is listing guest count we're also gonna pass bathroom count bathroom accounts listing dot bathroom counts like that all right and we're also going to pass location value from listing dot location uh value great and after I save this of course we're gonna get an error so let's just go back into components listings and create a new file listing info dot PSX great and I'm just gonna fix the error so listing info here and I'm gonna return as always uh just a div virtual play listing info like that and you can now safely go back into the listings listing ID listing client component and just import this from add slash app slash components listings listing info the same way you did with a listing head all right and let's just refresh this so the error gets away great and we have our text listing info right here so the component is working and now you can go back into components listings listing info right here and first as always let's write an interface but before we do that let's make sure to mark it as used Reliant nice okay and now let's write the interface so interface listing info props it's going to have user which is say user from add app slash types description which is a string it's going to have guest count which is a number it's gonna have room count it is a number as well and the same as bathroom count which is also a number it's going to have a category which is an object which is going to have an icon of Icon type which you can import from react Dash icons like I did right here it's also going to have a label which is a string it's also going to have description which is a string as well and I'm just going to add a pipe undefined here at the end and after that we're gonna have location value which is a string as well great and I'm going to use this listing info props to assign props to this react functional component and I'm going to extract all of these parameters right here so user description discount room count bathroom count category and location value like that great and now we're gonna have to extract our use countries code one more time so go ahead and write cons get by value from used countries and you can import that right here from add apps hooks use countries great and now usually we get the entire location but this time we are only going to need coordinates go ahead and write on coordinates or equal get by value location value question mark dot flat LNG like that make sure it's all a lowercase here great and now let's go and style this div right here so I'm gonna give it a last name of all Dash span-4 Plex Flex Dash call and gap-8 and inside that I'm gonna go and create another div and give it a class name of flex Flex let's Dash whole yeah and inside of that we create another div with the following class names so this is going to be a bit longer it's going to have that slash XL front semi bold Flex Flex Dash row item slash Center and gap-2 great and inside of that I'm going to create another div and I'm gonna write posted by user question mark dot name like that great and adjust below that what we're gonna do is we're gonna add so we can see how it's written hosted by Antonio and what I want to do here is I want to add my little Avatar here so I'm going to add an avatar component right here which you can import from a dot dot slash auditor and let's just uh make this neat and highly great and this Avatar if you remembers accepts a source prop so we can write Source user question mark dot image like that great great job and you can see how it used the default placeholder here all right so this is just a network error if that happens you you can just refresh all right uh and what we want to do next is we want to create another div just outside of this div that holds the user and the image so create another div inside and give it a class name of the following go ahead and write Plex Black slash row item slash Center Gap Dash 4 font dash light and text neutral Dash 500 like that great and inside of that press create one div which is just going to say yes account guests like this and then you can copy and paste this again and replace it with room count room count and instead of guests of course you're gonna say rooms and you can see how three guests already appear here is now three rooms appear here so just copy this div one more time and replace the last room count with bathroom count and the last label as bathrooms just like that great great job and now just outside of all of these three divs so right here at the end just write an HR line like that so now we have this HR line here and what we're gonna display here is our category so for that we're going to use this conditional category and end and we're going to create a new component called listing categories here and let's add some props to it so it's going to have an icon which is category that icon is going to have a label which is category dot label let's give it a description which is category dot description like that great and now let's go ahead and create this listing category uh component right here so it's also going to lay here in components listings folder so I'm gonna head and write listing category.tsx let's mark this as use client and let's just fix the error so listing category div as always listing category like that and now we can go back into listing info right here and you can just import listing category from dot slash listing category because they are in the same folder in components listings right here great great job you're doing so great uh and now we're gonna focus on working in this listing category here so open this up and first let's create an interface for this so I'm going to go ahead and write interface listing category rocks and it's gonna have an icon of Icon type which you can import from react icons it's going to have a label of a string and it's going to have a description of string as well and now let's just assign this box right here so listing category props and just extract all of them here so icon playable and description great now let's style the first div so last name Plex Flex Dash call gap-6 like that and let's replace this inner text here with another div we're just going to hold our icon so give it a class name of flex Flex Dash row item slash Center and GAP Dash four great so inside first element we're going to add is an icon and where do we get this icon well we don't import it from anywhere we actually just remap this icon as a capital icon here so we create an alias and then we can use this as an icon like that and let's give it a size of 40 and a class name of text Dash neutral Dash 600 like that great and just below this icon you can see how a little beach icon appeared here and just below that I'm gonna write another div and I'm going to give it a class name of flex flat slash poll and inside of that I'm going to create another div with a class name of textlash LG phone semi bulb and what we are going to render inside is label and below that we're going to create another div the class name x dash neutral dash 500. light and we're just going to write description like this great so now we just wait a second for this to reload and you can see how nice our category is being displayed here great and now we can go back into listing info so go ahead and go into app components listing the info right here and let's continue to work so just above just below this category conditional go ahead and create an HR element so we have another divider here okay let's just wait for this to render great so we have another divider here and now let's render our description so create a div and let's give it a class name of text LG phone dash light and Text slash neutral Dash 500 and all you need to render inside is description like that great and now you can see how we have our description as well and just below this div create another HR element so we need another divider and what we will do now is we're going to add our map but remember we have a special way we have to import our map from so just above this interface in listing info component props uh what I want you to do is write const map is equal to Dynamic and you can import Dynamic from next slash Dynamic right here so we're going to create a dynamic import and open an arrow function and write import dot dot slash map right here and just open the third object right here and write SSR Falls like that great and now you can go all the way uh to the bottom here and just below this HR divider you can add map Center coordinates just like that and you can save the file and just wait a second for this to refresh and there we go it's exactly pinpointed in Croatia uh where we actually are and now if I triangle somewhere else so I'm going to try and select this other listing which I created last time all right so if I click on this feeding right here if I load this individually you can see that it loads Sweden and it's uh uh we have a Countryside category here amazing amazing job so in the next part uh we're gonna add some loading States here so uh we're gonna add both the loading state for all of these items and we're gonna add a loading state for an individual item and we're gonna add a reservation date range calendar right here on this side let's continue by creating the right side of this individual listing view where we are going to display a calendar where users will be able to select a date range and Reserve this listing so I'm going to go ahead and I'm gonna collapse everything here and I'm gonna go into app listings listing ID and I'm going to select listing client component right here and I'm going to go all the way to the top here and just above this interface I'm going to create a constant uh initial date range like that and it's going to be an object which is going to have a start date of new date like that it's gonna have an end date of new date as well and it's going to have a key which has a value of selection just like that great and now what I'm gonna do is I'm gonna extract these reservations which we have in our types right here so I'm going to write reservations and one very important thing I'm going to give it a default value of an empty array so make sure you add reservations equals to an empty array right here in the props so you can always safely run dot map dot find and all kind of iterations without getting any errors your rate and just above this category here first thing I want to do is I want to add a login model so go ahead and write const login model equals use login model which you can import from add slash app slash hooks use login model like that great and make sure to execute this hook and then uh below that we're going to add the router so go ahead and write const the router is equal to use router from next slash navigation so just make sure that you import it from next slash navigation and not the next slash router and I'm just going to move it here along with the other packages great and now I'm going to write a constant disable the dates so const disabled dates it's going to be a used memo again open the function inside and don't forget to add the dependency array like that and what we're going to do inside is we're going to iterate over these reservations and see if there are any dates we have to disable for this listing uh because well there is already a booked reservation for that so we're going to write that by using let dates and I'm going to give it a type of date and an array so it's going to be an array of dates and by default it's going to be an empty array and now we're going to go over our reservations so go ahead and write reservations that for each reservation and let's give it the value of any for now and inside of that you can write const range is equal to each day of interval from date Dash F and S like that and I'm going to import that to the top here as well so make sure you import each day of interval from date Dash FNS like that and inside of that I'm going to open an object in this function to give it some options uh and what I'm going to write is start to be new date reservation dot start date like that and below that I'm going to write n to be new date reservation dot end date like that so make sure you have start date here and end date here uh great and now we have this range right here and all I'm gonna write these dates is equal to spreading the current dates and just spread the range like that great so what we're going to do is we're going to iterate over all of these reservations and we're going to create a range of dates uh between start and end date and we're gonna pass them to already existing dates array right here great and once we do that all I'm going to do is return dates like that all right and now let's just fill this dependency array here so we are missing reservations so let's go ahead and write reservations inside all right uh and I just want to replace this reservation we didn't have to use any here great so we can actually use the reservation type so then you will get an error if they start date or end date is incorrect so you should also get autocomplete start date and autocomplete dot end date like that perfect all right and just uh here what I'm gonna write uh I'm gonna add some states I'm going to add a function to create the actual reservation so first let's create some states const is loading and set is loading it's going to be equal to use state which you can import from react and the default is going to be pulse below that we're going to add the total price and set total price like this and use state by default it's going to be listing dot price and last one is going to be day three inch so I'm going to write date range and set blade range use State and default is going to be initial date range like that and I'm just going to give this a value of range and you can you'll be able to import that later so for now actually don't write anything here you can just write this all right uh and just below that uh I'm gonna write a function to create our reservation so go ahead and write const on create reservation is equal to use callback which you can import from react open a function and don't forget the dependency array and first things first uh if there is no current user we're gonna return login model on open so if we try and make a reservation and if we are not logged in we're going to open the login model great and then we're gonna set is loading to true because the process has begun and we're going to import axis from axis right here I'm going to add it to the top and we're going to make a DOT host call to slash API slash reservations like that and we're going to open this and we're going to add total price like that we're going to add a start date which is date range we got start date and we're gonna add end date which is date range that end date like that and a listing ID which is listing question mark dot ID like that and before we collapse this we're going to add a DOT then function right here uh which is going to trigger a toast of success so we're going to add a post from react code toast so I'm going to add that to the top as well like this all right and we're going to write those dot success this thing reserved like that great and then we're just gonna write set date range to be reset to initial date range like that and for now I'm gonna write a comment here uh redirect to uh Slash trips which doesn't exist yet so for now all I'm gonna write is rather that re-crash like this great and now let's write the cat function inside of that we're going to write post that error something went wrong like that and finally uh all we're gonna do is we're gonna write set is loading to false perfect uh and now of course we have to fill this dependency array so let's go ahead and let's write total price date range listing question mark dot ID we're gonna write the router current user whoops current user and we're gonna add login model like that perfect so later we're gonna redirect to trips but for now we're just going to refresh the router all right and now that we have this we can actually create a use effect just below that so go ahead and write use effect which you can import from react and add a dependency array right here and what we're going to do is we're going to change the total price depending on how user selects the dates in our uh calendar so I'm gonna write if date range dot start date and end date range dot end date and what I'm going to do is I'm gonna count how many days that is so I'm gonna write const Day Count equals difference in days and we're gonna pass in day range dot end date and date range that start date like that all right and just inside of that I'm gonna write uh if they count and listing dot price and I'm gonna write set total price to be they count times listing that price else I'm gonna write set total price to just be listing dot price like that and make sure you import this difference in days from date Dash FNS right here at the top right so what our use effect is actually doing is going to notice every time I make a change in my calendar and it's going to count how many days we have selected and it's going to multiply that by the listing price per night and if there is no day count all you can do is just display uh a single night price great now let's let's add date range and listing Dot price inside of that perfect and just before we continue I actually want to replace this difference in days with a similar function called difference in in calendar days so instead of difference in days make sure you import difference in calendar days from date FNS so what is the difference well difference in days will take account of the current time so if there are a couple of hours missing from the day it will not count that as a whole date and it's going to return as the wrong total price so I'm just going to replace it with difference in calendar days to remove the times in that calculation so your use effect function should look something like this in the end you should have a date range start a date Dent range end date and you're gonna calculate the day count using difference in calendar days you're gonna pass in the end date pass in the start date and then we're going to multiply day count by the listing price good rate and now we can go all the way here to listing info and just below that what I'm going to write is another div and that div is going to have the following class names we're going to write class name order Dash first mb-10 and the order Dash last and MD call Dash span three and inside of that we're going to write a new component listing reservation which does not exist yet but we are going to create it and of course if you save you're going to get an error but I'm going to ignore that for now and I'm just going to add all of the attributes which we will need so price is equal to listing dot price total price is equal to total price on change date is going to be a function which accepts a value and set date range passes the very same value all right then we're going to have date range which is paid range like that we're gonna add on submit which is going to call on create reservation which we created up there and we're gonna have disabled to be is loading and we're gonna have disabled dates to be disabled dates like that right and you can ignore this typescript warnings for now we're going to fix all of that all right so now let's go into our app into components into listings and create a new file called listing preservation.vsx great and inside of that uh uh first things first let's just write use client Here and Now what I'm going to do is I'm going to fix the error so this thing reservation and I'm just going to return uh an empty div like this I'm gonna go back into listings listing ID listing client here and I'm just going to import a listing reservation from add app slash components Slash listings slash listing reservation right here and I'm going to save the file so the error goes away great all right I'm going to go back into my listing reservation and first things first uh I'm going to create an interface here so go ahead and write interface this thing consideration props and it's going to be all of those things we passed in the listing client components so go ahead and write price which is a type of number uh date range which is going to be a range you're going to see that in a second so it's actually a different type of range this is an interface one but we're gonna import a different one later total price which is a number on change date which gives a value of range and returns a void on submit which is just an empty void function disabled is an optional Boolean disabled dates which is an array of dates great and just before we move on I'm going to install a package so we can import this range type right here so go into your terminal and you can shut down the application and just write npm install date Dash sorry npm install react Dash date Dash range like that great and you can now import destruct range from react Dash date Dash range uh just like that and we are missing the types for this so we also need another package npm install Dash capital d add types slash react Dash date Dash range like that so we actually get this range type and now if you wait a second in your code you can see that you have this interface range uh and it's exactly what we want here great so make sure date range is using this interface range which we imported from here and this value is the very same thing great and you can just go ahead and write npm run Dev again and just make sure to refresh uh your page great and now I'm going to assign all of these props to this component right here so react.fc like that and I'm gonna restructure all of the parameters so I'm going to write the price date range total price on change date on submit disabled and disabled dates like that uh great and now I'm just gonna wait a second for my page to compile so you can see the changes that I'm doing great so now the page has compiled and I'm going to start styling this side of our container great so let's give this a couple of class names here whoops like this and we're going to give it a BG of white rounded Dash XL border Dash square brackets one pixel border Dash neutral Dash 200 and overflow Dash hidden like that all right uh and now you can see a small little line here which is going to turn into a box so write another div inside and give it a class name of Plex Flex Dash roll items Dash Center Gap dash one mp-4 like that and uh now you can see a little bigger box here now inside of that create another div right here and we're going to say class name uh tax Dash to excel font plus semi bold and I'm gonna write a dollar sign space and just write a price prop right here and that will display our nightly price so this is a price per night if you remember we set a very large price for this second listing here great and just below that Dave I'm gonna create another one and I'm going to write class name on flashlight neutral Dash 600 and inside of that I'm just gonna write Knight so we know that this is a price uh per one night like that uh oh great and now just below uh the second div uh I'm gonna write an HR tag like that so we get another darker line here at the bottom and we're gonna create a new component here which is going to be called calendar so I'm going to write a calendar like this and I'm going to give it a prop of value which is date range and I'm going to give it prop of disabled dates which is disabled dates like that and I'm gonna write on change value and on change is going to be on change date value dot selection like that so make sure you have this calendar component right here with the value of the age range disabled dates disable dates and on change which is an arrow function which accepts the value and calls the on change date prop and gives it a value dot selection like that and if you save of course we're gonna get an error so let's go into components and let's go into inputs and create a new file calendar.psx like that and first things first I'm just going to fix the error so make sure you name this calendar like this great and inside I'm just going to return an empty div for now and I'm gonna go back into uh listings listing reservation right here and I'm going to import calendar from dot dot uh Slash inputs slash calendar like this and the error should go away I'm now going to go back into calendar and first things first let's mark this as use client here at top and let's write an interface so my interface it's going to be the uh sorry calendar props which is going to have a value which is a type of range which you can import from react Dash date Dash range it's going to have an unchanged prop which is going to send that value which is a type of range T dict which you can also import from react Dash date that Dash range and it's going to return an empty void and we're also going to have disabled dates which is a question mark So optional and it's an array of dates like that great and now inside of that I'm gonna assign this props here so I'm going to write react.fc calendar props like that and I'm going to extract all of those values so value on change and disable dates like that great and now instead of returning uh this div what I'm actually going to return is date range and you can import date range from react Dash date Dash range so make sure you don't import date range quicker that is another component and let's give this uh let's actually save this so perhaps we can already see something great and you can see that I see this very weird calendar here so first thing we have to do is we have to import some Styles so go ahead and write import react.state Dash range slash this slash Styles Dash CSS and below that import react Dash date Dash range slash disk slash team slash default.css like that and now if you save and just wait for this to compile uh we should see a better looking date great great job and now let's actually add some props here so I'm going to write range colors it's going to be an object with an array which is going to accept a single color which is uh 2 6 2 6 2 6 like that all right ranges is going to be an object which accepts an array like this a value like that date is going to be the default date which is new date like that uh on change it's gonna call the on change prop direction is going to be vertical and show date display is going to be pulse minimum date is going to be new dates so we don't want to select any of the dates in the past and disabled dates is going to be the prop disabled uh date like that great and if you save this and just wait for this to refresh you should see a bit of a different uh calendar right here and you can see how all of my previous days here are disabled and you can play around you can go forward you can go backwards great and then you can see that our date range doesn't exactly look uh doesn't fill the entire screen so to fix that let's go into a app right here let's close everything so go into app folder into globals.css and I want you to add dot RDR month and give it a value of width 100 and just add important uh at the end right here and also add dot RDR calendar wrapper a width of 100 with important as well and font Dash size is going to be 16 pixels important as well great and now if you save you can see that we have a completely different date right here great great job and just make sure you refresh your page so you see most up-to-date changes here on this calendar and you will actually be able to see how we can now select 9 and 13 16 and 19 but we cannot select uh disabled dates uh great great job all right and now what I want to do is I want to go back into listing reservation so we're going to go into listings listing reservation right here and just below this calendar we're going to add an HR component and I'm going to add a div below that like this with class name whoops with a class name of b-4 Plex Flex slash row items Dash Center justify Dash between Pawn Plus semi bold and tax Dash LG great and what I'm going to do inside of that is write a div which is going to say total and below that another div which is just going to add a dollar sign and write total price like this so now if you refresh uh you will actually see how we calculate our days now great so you can see how for one day the price is thousand for four days it's three thousand eleven thousand great so it multiplies by the number of days we selected great great job uh and what I'm gonna do next actually is I'm gonna go above uh just let's go back to this calendar right here uh and just below this HR tag before this total price I'm gonna write another div inside with a class name of p-4 and inside of that I'm going to write a button like this which you can import from dot slash button and let's give it a disabled of disabled let's give it a label uh Reserve and let's give it a on click on submit like that and you can import this button from dot dot slash button right here great so you can now see we have a nice button we can select we get the calculated price beautiful beautiful job and now let's just go back into uh listings listing client right here and you can see how we have an error here in this set date range uh use state so what we're gonna write here uh is we're going to add a pointy brackets here and add the range like that and you can import that from react Dash date Dash range as I did right here I'm gonna add it all the way to the top here great uh and let's go ahead and let's actually try this so I'm gonna open my network Tab and we should get an error of course because the API uh which you created here which points to slash API slash reservations does not exist yet but I just want to see how it looks so we're going to click on network here and I'm going to select I don't know four days like this I'm going to click reserve and of course we get an error but we did point to slash API slash reservations with the post and we get the 404 of not found so great that's exactly what we want because the route does not exist yet great great job let's continue and let's create our slash API slash reservations route so I'm going to go ahead and collapse everything and I'm gonna go into app API and let's create a new folder uh called reservations so go ahead and create a new folder inside API called reservations uh just like that and inside of there let's create a new file route.ts great and now first things last I'd like to import next response from next slash server like that let's import Prisma from slash app slash lib slash Prisma BB and let's import get current user from add slash apps access actions get current user now I'm gonna export async function post make sure you don't accidentally write uh export default that's what I did once and it breaks this route so make sure you do export async function post and open the function and we're going to write request Capital request like this great and first let's get the current user so go ahead and write current user is equal to a weight get current user that we imported above if there is no current user in this route in that case we're going to return uh next response that error like that great now let's get our body so cons body is equal away request dot Json like that now I'm going to extract all the values we need from the body and that is listing ID start date and date to price from body like that great and if there is no listing ID or if there is no start date or if there is no end date or if there is no total price we're gonna return next response that error like that great and now I'm gonna create a listing and reservation so the way we're going to do that is we're going to update the current listing and create a new version duration so we have this listing ID right here and we're going to use that like the following const listing and reservation is equal to a weight charisma.listing dot update where ID is equal to listing ID like this and data is going to be equal to reservations and inside of that you're going to write create so we're going to automatically link our relation between listing and this reservation and it's going to have the user ID of current user.id it's going to have a start date it's going to have end date and it's going to have total price just like that and in the end all we're going to do is return nextresponse dot Json listing and reservation great so that is our post request to create a reservation and let's just uh check if we did that correctly so I'm gonna go back into listings listing client right here so we send the total price we send the start date and date and listing ID great and now I'm just going to refresh this right here and I'm gonna prepare my mongodb in another tab so we can see if we successfully created it or not and let's go ahead and let's try and select a date range here I'm going to click Reserve and we get a toast success listing Reserve so let's go ahead and refresh this right here and you can see we have a new reservation with total price with start date and date created at user ID and listing ID great great job okay uh now what we have to do is if you go ahead and see where I reserved so I picked one of these dates we now shouldn't be able to pick those dates again because they've just been reserved so that is what we're gonna do next we're gonna go ahead and I'm gonna close everything here and I'm gonna go into app actions and I'm gonna create a new file called get reservations.ts so this is going to be an action for a server component so not an API call so import Prisma from add slash app slash lip slash Prisma DB like that and let's write the interface I parents uh we're just going to accept a listing ID which is listing ID which is an optional string user ID which is an optional string or author author ID which is an optional string so we're going to use these params because we're going to use this get reservations route not only in this screen right here but also in our my reservations and my trips are out as well so we're gonna have to be able to uh query them either by this listing if we're loading them there by user ID if we are looking at let's say my trips or by author ID if we are going to query uh in my reservations so we can see all the reservations that someone else made on our property because we are the author of that property all right now go ahead and write export default async function get reservations and inside of that I'm going to write params and that's going to be equal to I parents with the capital P so I parents like that uh this basically this will be defined up here great and open this function and first let's extract all of our parameters so that is listing ID user ID and author ID from parents like that and then we're going to write const query give it an uh any type and open an empty object if we have a listing ID in that case query.listing ID is going to be listing ID if you have user ID in that case query that user ID is going to be equal to user ID like that and if we have author I be in that case what we're gonna have is query dot listing is equal to open object user ID author ID like that so you can see depending on whether we send a listing ID user ID or author ID we're going to query by different things so if we send the listing ID we're going to find all reservations for this single listing that we're looking at if we search by user ID we're going to find all of the trips a user have and if we search for author ID in that case we're going to search for all of the reservations that other users made for our listings great and now that we have that we can start and write our reservation bench function so write cons reservations is equal to 08 Prisma dot reservation dot find many and you're just gonna write where is equal to query which we modified up there we're going to write include listings to be true and we're also going to order by we're gonna order by created at to be the sending great and technically all we can do now is uh return save return reservations but you already know that we're gonna get some errors because we are sending date objects so instead of that let's write const safe reservations and write reservations.map reservation and go ahead and open an immediate object so inside of here I can just spread the current reservation like that and first thing I'm going to fix the created ad which is going to be reservation dot created at dot to ISO string like that below that I'm going to fix start date which is reservation dot start date.2 ISO string as well then I'm going to do the same for end date so reservation dot end date.2 ISO string like that and then I'm gonna fix the listing so I'm going to open an object for listing and inside of that I'm going to spread reservation dot listing like that and I'm going to fix create an ad to be reservation.listing dot created at dot two ISO string as well great great job and now we can use that safe reservations to return save reservations like that and one thing I want to do is I want to have this in a try and catch block so I'm gonna go here to the top and just before we extract our parameters just write try to open this block and before we return I'm gonna end that block and then we can uh select all of this and just indent it so it's clear that this is in the try block great and I'm gonna write catch error give it a type of any and we're gonna do throw new error error like this if anything goes wrong great great job so just to recap we're getting listing user ID out ready depending on that we create a different query we find all of the reservations depending on that query we included the listing because we're going to need it in one of the cases we order by created add and we sanitize the date objects so we don't get any uh errors regarding that great and now we can go back into our app into uh listings page.tsx and I'm gonna go and I'm gonna call those reservations so right below this listing I'm going to write const reservations is equal to a weight get reservations like this I'm gonna pass in parents from that and in this case our parents is just a listing ID so if you look at the code what they're going to do is we're going to search reservations for this current listing and then we can use these reservations uh to pass them in this listing clients the reservations is going to be equal to reservations uh liked it and of course we get an error because we sanitize our string but in listing a client if you go ahead and go into listings ID listing client right here you can see that we expect an array of reservations so we're just going to fix that and instead of reservation we're going to write safe reservation so for that we have to go back into here types right here and just go ahead and create export type save reservation like that and just write omit open pointy brackets for the first parameter I'm gonna write reservation which you can import from ad slash at Prisma slash crime right here and for the second I'm going to write created at by start date by end date and we're gonna also fight listing like that great just type in and signed and open an object and let's write created at is going to be a string start date is going to be a string and date is going to be a string and the listing is going to be safe listing which we defined here above great and now we can go back into listing the client right here and instead of reservation we're going to use Save reservation which you can import uh from let's see where we import that from add slash app slash types right here where we also have our save user and safe listing and you can remove this import reservation from Prisma slash client and now the error Here Is Gone great and I'm just going to refresh this page now and now I'm gonna check uh if I can still book over at my reservation so let's see in database I created the start date uh in the fourth month first of April to 5th of April I think if I'm reading this correctly and if I try you can see that I have a blocked out reservation right here let's see if that is correct uh yes I think that that is correct I'm going to try again so we're going to select 16th to 19th I'm gonna click Reserve right here and I'm going to refresh you don't have to worry that we are refreshing right now uh that is because later we're gonna redirect to a different route and you can see that I have 16 to 19 books great and if I try 24 to 27th and click Reserve again and refresh let's see if that is booked as well uh great amazing amazing job you finished uh creating reservations and loading reservations let's continue and let's create our slash trips route right here so we can actually see how we redirected that in action so I'm going to start by collapsing everything here and in my app folder I'm going to create a new folder called trips like that and inside of there I'm going to create a new file called page.dsx great and now inside of there what I'm gonna do is I'm gonna import a couple of stuff so first we're gonna import empty state from dot dot slash components slash empty state I'm going to import client only from DOTA slash components client only I'm going to import get current user from dot dot slash actions get current user I'm going to import that reservations from data slash actions slash get graduations and I'm going to write this component name so constrips the page is going to be a synchronous and it is a server component so we're not going to write use client at the top and first thing I'm going to write here is const current in user is equal to away yet current user like that and if there is no current user which actually should not happen because we're going to use the middleware that is going to protect this route from annotating users but just in case let's write return statement here and let's wrap this in client only like that and inside I'm going to use empty State component which we have imported here at the top and I'm going to write a title of unauthorized and a subtitle of please plug in like that great and now I'm gonna call our reservations so const reservations is equal to a weight get reservations from our action imported above and if you remember it accepts a different parameter so in in this case I'm going to write user ID I'm just gonna collapse this so user ID is going to be current user.id like that great uh and what I'm gonna write next is an empathy state if there is no reservations that this current user made so I'm going to write if reservations that length is equal to zero in that case I'm going to return again client only empty state and we're gonna write for the title here is no trips account and for subtitle I'm gonna write looks like you haven't reserved any trips like that great and now for the final return function if there are reservations that this user made we're gonna return again client only and inside of that I'm going to write Crips client like this and I'm gonna pass it a couple of props so it's going to have reservations and it's gonna have current user like that and if we save of course we're going to get an error because this trips client does not exist so inside the strips folder I'm going to create a new file trips client that's the SX adjust like that and first I'm just going to fix the error so it trips client like this and I'm going to write a div my trips like this and we can go back into page.tsx in the trip folder and just import that at the top uh right here like that great and now uh what I'm gonna do is I'm just gonna add a slash trips to this sidebar here so let's go into uh components navbar user menu right here and for my trips instead of an empty function I'm going to write router and make sure you import the router so here at the top I'm going to add const router it will use router from next slash navigation so just make sure you import it from next slash navigation and not next slash a router great and now that we have the router in this empty function which says my trips I'm going to write router.push and I'm going to write the slash trips like this great so now after I've saved that I can try and click on trips I'm gonna try and refresh first maybe it has not updated yet so let's just wait for this to compile and I'm gonna click here for click on trips and uh in a moment we should be redirected uh to slash trips uh okay and we get an error so let's go back into our uh pages so back into trips page.tsx and yes we have the trips page but we have not exported it so just make sure that you are in the trips folder so in app folder trips folder page.psx oops and just make sure at the bottom you write export default tricks page like that great and now this error should go away let's just wait a second for this to recompile all right any moment now and there we go we can see my trips right here so what does that mean that means we bypassed this current user so we are logged in and there is no empty reservations because we actually did make some reservations uh in you can see that I have a bunch of reservations here and they all share the same user ID and that is the current user that I'm logged in with great and now let's go and let's edit this Trip's client component right here and don't worry we're not gonna need to create any new components we're going to reuse our listening card component so first let's just write an interface Trips Trips client props like that and I'm gonna write reservations to be a safe reservation which you can import from dot dot slash types and the next one is going to be current user which is an optional type of safety user or now like that great now let's just assign these props so react.fc is equal to tricks client drops like that and just extract these values so reservations and current user like that all right and now uh first things first let's replace this tip with container like this you can import that from dot slash components dot slash component slash container here or you can use the uh app abbreviation if you want to all right so like this and inside of this container I'm going to import heading also from data slash components slash heading it is a self-closing tab let's give it a title of trips and subtitle of uh where you've been and where you're going like that and if we save that we should get that new heading inside our container great great job all right and now we're going to write a div just below this setting right here and we're going to give it the following class names so right class name and class names are going to be mp-10 grid grid Dash call slash 1 SM grid dashboard two MB grid Dash course Dash three LG grid Dash calls Dash four Excel grid Dash course Dash five to excel grid Dash calls dash six and get Dash 8 like that great and before we map over our reservations right here I'm gonna write a couple of actions here so first let's also import our router so go ahead to the top and right import uh use router from at next slash sorry from next slash navigation like that and since we're using a hook here make sure to mark this component as client at the top great and now we can go here before the return and write const router is equal to use router like that just confirm you're importing the router from next slash navigation all right below that I'm going to add a State field so const the leaping ID ID and set deleting ID is going to be equal use state from react and by default is just going to be an empty string like that and you can import new state from react I'm going to add it right here to the top and now I'm going to write my on cancel function so const on cancel it's going to be use callback but you can also import from react right here at the top and uh what this is going to accept is an ID of type of string like that open a function don't forget the dependency array and first things first we're gonna add set deleting ID to be this ID from our parameters right here in this function and then we're going to write axis which you can import from axios I'm going to add it to the top right here and write axis dot delete this routing does not exist yet but we're going to create it in the next part so just write this template liberal strings right here slash API slash reservations slash and open a special object ID like that and then write dot 10 open a function and we're going to write post which you can import from react Dash hot Dash those I'm going to add that to the top here and we're going to write post that success uh reservation cancel it like that and I'm gonna write dot catch error and I'm going to write those dot error error question mark dot response question mark dot data question mark dot error like that so we read the actual error from the API call and on the right Dot finally here uh we're just gonna write set deleting ID to just be an empty string again great and let's just not forget uh to also call uh not here but after in this dot then where we send the toast.success make sure you write router dot refresh like this so we get the most up-to-date data here and that means we have to add router to our list of dependency uh array great great job and now we can go back here and inside this grid div open uh object and write reservations dot map reservation and you can just go ahead and immediately return a listing card which you can import from you're gonna see now I'm gonna go to the top make sure you just indent this a little bit okay so you can import listing card from dot dot slash components slash listing slash listing card we already use this in our index page so we can reuse it here and remember we added some additional secondary labels here you can see it has action ID it has action label on action and we didn't use that until now we also didn't use the reservation so we're gonna use all of that now we're going to write key reservation dot ID we're going to write data to be reservation dot listing we're gonna write reservation uh to be reservation we're gonna say action ID is equal to reservation dot ID on action is going to be on cancel uh disabled is going to be deleting ID is equal to reservation dot ID action label is going to be cancel reservation and current user is just going to be passed as current user like that and let's just save this now and see if we load our trips and you can see I created a couple of trips here from May 1st to May 4th from April 24th to April 27 from April 2nd to April 6th so I have a bunch of reservations here for the same listing uh great and uh see we have an error here which says the safe reservation is not the same number the type of a reservation and undefi so let me just go ahead and quickly resolve that so it's a very simple fix let's go into our components into listings and listing a card and here instead of writing reservation we're going to use Save reservation because we created that in the previous part so just write save reservation here when you can import it from ads types so now you should have safe listing save reservation and save user here and you can entirely remove these imports from Prisma slash client right here great great job now if for any reason you're not seeing cancel a reservation here so if this is not appearing for you or if these dates are not appearing for you uh let's go back into listing card and let's see why they should appear so we have this props reservation on action disabled action label action ID and current user so all of those actions are needed uh to display these items right here so in order to have handle cancel we have this on action prop right for price we if there is a past reservation we're going to use the total reservation price you can see in database that is a calculated reservation by the nightly price times how many how long this start and end date lasts right but if we are at our index page you can see that since we're not passing any reservation there uh what's gonna happen is you're just gonna see the nightly price see see we here we have a thousand dollars per night right but if I go into my uh trips you can see that I have three thousand three thousand three thousand four thousand so it's a multiplied price right and let's see where this actually displays so yeah this button displays if you have on action and on label and uh this reservation so you should not see the label per night because this is not per night so we uh hide this if it is a reservation type and instead of this playing category here if we have a reservation date which is a constant we defined here which also depends on the reservation prop where we calculate uh the format uh here great so if you for any reason you're not seeing this exactly as I am just go into your listing card and make sure that you have all of this uh the same as me great great job uh and now what I want to do is I wanna add uh I want to go back into components listings listing reservation right here let me see if I did that correctly sorry not here but let's go back uh into our listings listing a client here great and here is the function we have uh oncreate reservation and remember I added a comment here so instead of router that refresh what I'm going to do is I'm gonna write router that push slash trips right here and I'm Gonna Save that and I'm gonna refresh this page and now I'm going to create a reservation for this listing that I have right here so just let's wait a second for this to compile all right and I'm going to go ahead here I'm going to select from 9th to 12th and I'm gonna click Reserve and after that I am redirected and I'm right here and you can see from 9 to 12 right here amazing so in the next part we're going to create an option to cancel this reservation great job so in this part we're going to create a route that is actually going to be able to cancel this reservation because if you try and click right now we're going to get an error that something went wrong because um there is no API that exists there and we're also going to create a slash my reservations route right here so let's go ahead and let's uh close everything and I'm gonna go into app into API into reservations and I'm going to create a new folder square brackets reservation ID like that and inside of that I'm going to create a new file route.ds great so first let's import next response from next slash server let's import that current user from add slash app actions get current user and let's import Prisma from add slash add slash Libs Prisma BB like that great now I'm going to write some uh interface for this so interface I parents is going to be reservation ID question mark string like that and now let's write export async function delete so just make sure you don't accidentally write export default make sure it's export async function delete right here and inside of that write a request which is a type of request and the structure params and give it a type of params I parents which we defined right here at the top great now open this function and first let's fetch the current user so cons current user is equal await get current user like that if there is no current user in that case we're going to return nextresponse dot error like that and now let's just extract the reservation ID so right const reservation ID is equal from parents like that great now let's check the if this reservation ID is valid so if there is no reservation ID or if type of reservation ID is not equal the string in that case we're gonna throw new error invalid ID like that great and now let's just write const reservation is equal to await Prisma dot reservation dot delete many the reason we want delete many is because we're going to use some special queries with that so go ahead and write where ID is equal to reservation ID and then just after this ID you're going to write capital or like this and open an array and first object is going to be user ID current user.id and just below that um sorry which is just below that open another object and write listing open an object inside user ID current user dot ID so what does this mean well we want to ensure that the only people who are able to delete a reservation is either the creator of reservation or the creator of the listing that the reservation is on so we want to enable the owner of this house in Sweden to be able to cancel any reservations that was made on their property and we also want to enable the person who made the reservation in my case me to also cancel my very own reservation great and you can just return nextresponse.json reservation like that great and now if you refresh this you can actually try and delete the reservation and let's see where the the code for that is so you can go into Trips Trips client right here and this is where we have it so we call axis dot delete API slash reservations and we pass the ID of the reservation here so I'm gonna head and click uh cancel reservation on this Europe Croatia reservation let's just wait a second okay and we get an error let's see uh what that is about okay uh I'll just refresh my page uh I don't think there's anything wrong I think this is just a development error so let me try again and just like that we get a reservation canceled and it's removed from here so the error was absolutely nothing it might not even happen to you uh it was just a development issue I probably did not refresh uh on time great and you can try and remove other reservations so I'm going to try and remove this one you can see how it's just less and less and one cool thing I want to show you right here so I'm gonna cancel this one and I have this reservation from April 2nd to April 6th so let's go into this reservation right here and I want to show you something so just a second so this loads right here all right uh and I'm gonna go here into the calendar in April and you can see how I have blocked out the dates from second to sixth because the reservation exists but if I go into my trips right here and if I cancel this reservation here and if I go back and select uh the very same Sweden um listing let's just wait for this at a slash route to load if I go back into this Sweden listing here and if I go into April you can see that I can now select these fields so I think that's really really cool let me try again let's try it from 16th to 22nd and click Reserve all right now I'm going to get redirected here so from 16 to 22nd and if I go back here you can see that this entire week uh is booked right and I cannot select and Reserve anything uh uh in this range but if I go back into my trips right here and if I cancel the reservation and then go back to my uh listings and click on Europe Sweden again you can see that it's completely freed up uh this booking great that's exactly uh what I wanted to do all right and now what I want to do is I want to create this my reservations page so in order to do that I'm going to collapse everything again and we're going to do a pretty similar thing that we did with uh trips folder right here so go ahead and create a new folder and we're going to call it reservations like that and inside of that uh create a new file page dot the SX like this great and now you can actually uh let's actually just write something here so first things first let's import empty state from dot slash components uh empty State let's import the client only from that components client only let's import get current user from action screen user and let's import uh get reservations from dot dot actions get reservations good and now I'm going to write const reservations page which is going to be a synchronous function like this and just make sure you export default reservations page as well and inside of here of course I'm going to patch the current user so away get current user so very similar to what we did in the trips page if there is no current user we're going to return a client only component so client only like this and inside of that I'm going to add empty state and write title unauthorized subtitle please log in like that great and now uh after this if it was I can fetch my reservations but be very careful how about to do this so const reservations is equal to away get reservations and remember we can uh pass different parameters here so our parameters can either be listing ID user ID or author ID so in this case we're going to pass author ID because we want to load all reservations on our listings not our trips but all the reservations that other people have made on our listings so just write author ID current user dot ID like that great now let's write the empty state so if reservations dot length is equal to zero return client only again and inside of that we're going to write another empty state and we're going to give it a title and subtitle so go ahead and write title to be No Reservations found and subtitle do looks like you have No Reservations on your properties like that great and in our uh final return we're gonna write plant only like this again and for now I'm just gonna say uh reservations client like this and we're gonna pass it reservations so very similar to what we did with trips reservations and current user current user like that great and now I'm gonna go here and just write reservations client that's the SX and I'm just going to fix the error so reservations client and just div iterations on my properties like that great and now we can go back into reservations folder page that PSX and just import this right here at the top so reservations client from dot slash reservations a client great and now I want to enable that route when we click on my reservations here so I'm going to go uh into components into navbar into user menu right here and just the same as we did with router push trips we're going to copy that and paste it in my reservations here so rather that push slash reservations like this basically the same name that you added this folder right here reservations great I'm going to save this I'm going to refresh once and after this has compiled let's just wait a second so everything is ready a couple of more seconds should do all right and now once I click on my reservations and wait for the page to compile don't worry that everything is so slow in development this is because we have not uh compiled these Pages beforehand in production everything is going to be compiled and you can basically see how we have the empty State No Reservations found looks like you have No Reservations on your properties uh great you can actually also check my trips and I think since we removed you should get the same empty message but let's focus on my reservations so just ensure that you're in the slash reservations route right here great and now let's go ahead and let's create this reservations client the component so go to the top and just write use client here and you can import toast from react Dash hot Dash toast you can import axios from axios you can import use callback and use state from react you can import use router from not next slash router but next slash navigation so make sure you don't have that mistake let's import some types so save reservation and save user from dot dot slash types let's import heading from dot dot slash component slash heading let's import container from dot dot slash components slash container and let's import listing the card from dot dot slash components Slash listings slash listing card like that great and now I'm gonna go ahead and I'm just gonna uh remove this div right here I'm going to write container like that container all right and inside that I'm gonna add a heading like this oops with a title of reservations and a subtitle of bookings on your properties like that so proper is like this great and in order to see this let's go and let's create a reservation right so we are not looking at this empty state so I'm going to go ahead again in Europe Croatia here and I'm gonna book uh from second to Fifth and click Reserve right here that is going to redirect me uh to slash trips but I'm gonna go to my reservations here all right and now you can see that we have a different view here we have reservations booking on your properties great and now before we map over our reservations let's first write the interface so the interface is the following interface presser patients client props is equal to reservations safe reservation like that which is an array and the current user is optional say user quite null like this great now let's just assign these props here so reservation client props and I'm gonna extract these values so reservations and current user like that great now I'm just going to write const router is equal to use router here which we imported from next slash navigation const again deleting ID set deleting ID is equal to use State and default value of an empty string and we can reuse our on cancel here so just write const on cancel is equal to use callback just write an empty function here and don't forget the dependency array so the parameter we're going to take here is ID which is a type of string and we're going to say set deleting ID could be this ID right here great and now let's just write axis dot delete and open template literals like that slash API slash reservations slash and open the ID like that right dot then inside of here we're going to write toast.success reservation canceled and router.refresh like this on catch we're gonna write post that error something went wrong like that and finally we're going to remove the deleting ID so set deleting ID it's just going to be an empty string like that and in the dependency rate don't forget to add router your great great job okay and now we can actually go here and create another div inside of here and let's give it a grid class name like we did with the trips so class name is going to be empty-10 grid grid Dash call slash one SM grid Dash 4 and the grid whoops MD grid Dash call slash three LG grid Dash called slash 4 XL grid Dash calls Dash five do excel grid Dash course dash six so just like that and gap-8 great and now let's iterate over our reservations so reservations.map preservation and immediately return the listing part component which we also used in trips let's give it a key of uh reservation dot ID let's give it a data of reservation dot listing let's give it the reservation of reservation like that action ID to be reservation dot ID on action is going to be on cancel like that uh disabled it's going to be deleting ID equal to reservation dot ID and action label is going to be cancel guest sorry to cancel guest reservation like that and current euser is going to be current user like that perfect great and now if you wait a second uh the reservations should appear here maybe you can try and refresh yeah we have it right here so right now you're probably not seeing any difference between this and between the trips page so in order to see the difference I'm going to show you uh how to do that so uh instead of uh I'm going to create another user here so I'm going to log out here just wait a second and I'm going to create a new user which is going to create a booking on one of our properties so I'm gonna click sign up here I'm going to create a user called uh new one.mail.com new one and one two three three to one as a password I'm going to click continue once our user creates oh my bad so I have an error because I did not refresh on time if this happens yeah it's because the app folder is still experimental so sometimes the page completely breaks uh if you really want to fix this without refreshing all the time you can just shut down the application like this and run npm run Dev again perhaps this did not happen to you at all so you can just continue normally all right so everything is fine now and I'm just gonna try uh and sign up again so I'm gonna write new one mail.com new one and my password I'm gonna click continue here and let's just wait uh okay and let's try login now so new one mail.com great and I am logged in great uh uh now what I'm gonna do is I'm gonna go into Europe Croatia for example here let's just wait for a second so the listing loads all right and I'm going to create a booking from 9th to 11th like that I'm gonna click Reserve and now we will be redirected to our slash trips page let's just wait a second for to compile great and so these are my trips as the owner of reservation but I'm not the owner of this listing so if I go into my reservations here let's just wait for it to compile what you're going to see is that we Have No Reservations found I only have trips but I don't have reservations and what I can do now I'm gonna log out again and I'm gonna go and log in as Antonio so let's just wait a second I'm going to log in as Antonio mail.com whoops I'm going to click continue here all right I'm logged in and now if I go into my reservations here you can see that I have uh when we selected from second to fifth I think right so I can actually cancel guest reservation here so I'm going to cancel all of this here I'm going to cancel this one let's say for this refresh I'm also going to cancel this one because I'm the owner of this listings I'm going to log out again now and I'm gonna go back uh into the new user so you can see that their reservation has been canceled because I as the owner have canceled it so I'm gonna write new one mail.com one two three three two one and click continue and if I go into my trips you can see that I have no trips because the owner of the listing which is Antonio in this case has removed my trip amazing amazing job and just before we wrap this up I noticed something so I want to log out here I'm just gonna refresh everything on localhost yeah and one thing I noticed is that when we sign up uh the login model did not appear and I want that to happen so uh let's just close everything and go into app into components into models into register model right here and let's find this action yeah so once we successfully do the axes that post API register let's also just add um login model dot on open like that just below this so after we successfully register close the register model and open the login model that's going to be a much better experience for the user right so I'm going to try that now I'm going to refresh everything let's just wait a second so it compiles all right and I'm going to try and create a new user so I'm going to write new three mail.com new three and I'm going to click continue and great so after it has been success I can actually log in again but before we do that let's also add a success message here so post that success let's say um let's just find success like that great so I'm gonna go back to create my account and I'm gonna register with new four and new four here click continue great so now I have the success message and redirected here and I can try a new core and continue logging in great and I am now logged in as new for amazing amazing job so just to recap what we did we fixed the login model uh problem well it's not exactly a problem but a better user experience and we finished the my reservations and my trips so we can now create trips we can create reservations we can create bookings and we can also cancel them uh all that's left now is to create my reservations my properties and last thing we're gonna uh create a model uh to enable all of these filters in this main search right here amazing amazing job all right so now we have this slash favorites route that here that is not used but we have all the functionality needed for the favorites so first things first I'm gonna close everything here and I'm gonna go into app into components into navbar user menu and just the way we did with reservations I'm gonna add four favorites here so I'm gonna write router.push slash favorites just like that okay and now let's create the page for the favorites so go into app and create a new folder called favorites like that and inside of that create a new file page.dsx great and you can import empty state from components empty State and you can import client only from components to client only you can import get current user from actions get current user and you can import uh get favorite listings which actually we don't have yet so uh before we do that let's just finish writing this silicon's listing page is an asynchronous function which for now it's just going to return a client only like this and I'm just going to write empty state and let's give it a title of no favorites found and the subtitle of looks like you have no favorite listings like that and make sure you export default listing page at the bottom uh right here okay and now you can try and click on my favorites here and after it compiles you should be able to see uh Slash favorites with our empty States like that so just make sure you're at localhost 3000 at sorry slash favorites like that okay and now let's go into our actions let's go into actions and click new file and create get favorite listings plot TS like that and I'm gonna import Prisma from add slash app slash Libs Prisma BB like that and I'm gonna import uh get current user from dot slash get current user because we are in the actions folder so we can access it very easily great and now just write export default async function get favorite listings and open a try and catch block and in the try block write cons current user is equal to a weight we get current user like that and just write if there is no current user just return an empty array because there are no favorite listings to load and now let's find our favorites so for that we're gonna write const favorites are equal to await Prisma dot listing dot find many where ID open an object and write in open an array and you're gonna spread parenthesis current user dot favorite IDs pipe pipe empty array like that great and uh what we have to do now of course is sanitize our favorites so just write const save favorites is equal to favorites.map favorite and just return an immediate object so you can spread favorite inside of that and just fix the created ads to be favorite dot created at let's do ISO string just like that and then feel safe to just return saved favorites like that and in the catch block give it a type of any and just write throw new error error like that and just make sure you actually don't misspell this so just return this save favorites from here great and now you can go back in a page.tsx here and you can actually import yet favorites like this from dot dot slash action slash get favorite listings uh great great job okay and now I'm gonna load my listings here so I'm gonna write const listings equal await get favorite listings const current user await to get current user like that and first things first instead of just returning this I'm gonna write if listings length is equal to zero uh in that case we're gonna return this which we created right here so just in them that a little bit great and otherwise we're gonna return a client only like this and inside of that write favorites client like that and favorites client is gonna have listings and current user like that great and you can save this uh and if we had any favorites we would have gotten an error so let's just go and make sure you favorite something because you need to get an error here so I'm going to click save here sorry I'm going to click like here so it becomes uh my favorite okay let's just wait for this to reload this is not an error this just happens with next 13 app router okay uh and now that this is loaded let's just go into my favorites here so it loads localhost at 3000 uh Slash favorites and we get an error favorites client is not defined great so let's just go into favorites where we have our page.dsx and let's just create a new file whoops new file favorites client that the SX like that and I'm just going to go ahead and fix this error so favorites client and I'm just going to write a div like this and now I'm gonna go back into page.dsx and I'm just going to import this from dot slash favorites client like that and I'm Gonna Save this and now we're gonna get rid of this error uh once the page compiles great and we have our favorites client text right here so let's go back into our favorites client here uh and let's uh actually uh let's add an interface for this first so interface favorites client props is going to accept listings which is an array of safe listing which you can import from dot dot slash types so make sure you put this little array at the end and of course you have current user a question mark saved user which can also import whoops which can also import from data slash types and just add a note here great and now let's add these props here so react.fc favorites client props we're gonna have listings and current user in the props right here instead of this div let's just return a container which you can import from components container great inside of that let's add a heading which can also import from components heading right here great and let's give this a title of favorites and a subtitle of list of places you have favorited like that great so now instead of this favorites client we should get that header great it's right here and now let's just write our grid here as usual so class name it's gonna have a neat empty 10 grid grid Dash coals-1 SM grid Dash called slash two MD grid Dash calls Dash three LG grid Dash called slash or Excel grid Dash holes Dash five and two Excel grid Dash calls dash six you can also copy and paste this from uh either trips or reservations and GAP Dash 8 like that and we can safely now either it over this so just write listings.map listing like this and immediately return a listing record which uh whoops listing card like this which you can import from dot slash dot slash component slash listing slash listing card and just give it a current user give it a key of listing.id and give it a data of listing uh like that great and now if we wait a second we should see our new yeah there we go and if I go ahead and go into uh and favorite Sweden for example await a second that it triggers the API call all right so it's success and it's going to refresh great and if I go into my favorites uh again now it's gonna load new favorites like that and I can remove them from the favorites as well great that's it we finished our favorites uh great great job all right and now let's create the last page in this navigation bar my properties so let's close everything up and let's go into app components navbar user menu here and instead of an empty array here sorry an empty function just write router that push slash properties like that great and now in order to start working on properties we have to go and head into our get listings action which we already use to fetch all listings on the main page and I'm gonna go ahead and create an export interface I listings params like that and I'm going to write user ID question mark string like that and in here I'm gonna accept those parents like that and right eye listings params just like that great and now instead of just fetching all of the listings we're gonna get add an option here uh so first things first I'm going to extract user ID from params like that and then I'm gonna write let query any is equal to an object like that make sure you write let and not cones because later we're gonna need this for something else but for now you can just use an if Clause if there is a user ID just write query dot user ID is equal to user ID like that and make sure your if Clause checks for uh user ID just like this and now instead of just having this order by we're also going to have where query uh just like that and everything else can stay exactly the same and just test on your or a slash page if everything is okay so okay we have an error here let's just check that all right so what I want you to do is close everything and go back into app page.dsx here where we actually use this get listings you can see we have an error here so what I want you to do is I want you to create an interface on props like that and write search params which are going to be I listing params which we just created in this action get listings so this thing we exported right here make sure you export it so in this actions get listings make sure you have interface eye listing params and you export it from that component so we can import it here by the structuring like this so we imported I listing terms from dot slash actions get listings alongside our get listings function like that great and now inside of here I'm actually going to change this a bit so I'm going to write constant home is equal to async function like this and just remember to go to the bottom and write export people home like that great and now inside of that what I'm going to write is search programs home props like that great and now I can use these search params to pass it as an object here great and now if you go ahead and refresh the error should go away um because search params in server component is always an object regardless if there is nothing in the URL for now but this is already a sneak peek in how we are going to use these filters to actually filter by category here great great job okay uh and now what I want you to do is I want you to close everything and I want you to copy uh sorry go into app folder and you can just copy trips for example so just go ahead and copy trips and paste it in the app folder and rename it to properties so we don't have to write everything that's going to be very similar here so go into page right here and first thing I want you to do is to rename the strips page to properties page like that great great job all right so first we're going to fetch our current user as always unauthorized can stay exactly the same now instead of reservations we're going to call listings and instead of get reservations we're going to use get listings actions so import get listings from dot dot slash actions get listings and you can remove get reservations here great now the user ID will stay exactly the same we just added that in the function instead of reservations.length zero is going to be listings.length and the title is going to be you know properties no properties found and we're going to write the subtitle of looks like you uh I have no properties like that great and now instead of trips client we're actually going to use a page we're going to use properties client here so just go ahead and rename the strips client to properties client like that go back into page.dsx and just rename this to properties client and the import is dot slash properties client as well and instead of trips client just use properties client like that and properties client instead of reservations it's gonna accept listings and just pass in the listings here as well so go ahead and paste it like this great now let's go back into properties into properties client component and first let's just rename both the types and the component so I'm going to do that using this and just write properties client like that great and inside properties client I'm going to replace reservations with listings and instead of safe reservation is going to be say listing from types like this and you can remove safe reservation I'm not going to use that now we get an underline here because we're using reservations so just make sure you use listings well just like that now on cancel is going to be pretty similar actually so we can leave this exactly the same except uh the route slash API slash reservations we're actually going to be able to delete our properties so the route is going to be slash API Slash listings uh and this ID right here and instead of reservation cancel we're going to say listing deleted like that great great job all right and now in this heading right here instead of title trips it's going to be titled properties like that and the subtitle is going to be list of your properties just like that great and you can go ahead and try and go into my properties here so you actually see what we're doing and I'm pretty sure we're going to get an error uh actually we're not yes because we are in a different account here so uh let's just fix this error quickly so instead of iterating over reservations here let's actually use listings like that so listings.map and instead of reservation is going to be listing like that so it's going to have a listing.id data is going to be the entire listing like that reservation is not needed in this case the action ID is going to be uh listing dot ID on cancel is going to be uncancel and disable is going to be deleting ID is equal to listing.id and instead of cancel reservation is going to be read property just like that great the reason I'm not seeing any properties is because I'm in the user that has no properties so I can either create one or I think it's much simpler that I log out and log in into a user which has some properties all right so I'm going to click login here and I'm going to go into Antonio mail.com and login great and you can see I have a list of my properties right here amazing and now I have to create a route for this delete properties because if I click now we're gonna get an error because this slash API Slash listings ID does not exist so let's go into our app API folder into listings right here and create a new folder square brackets listing ID and inside of there create a new file route.ds great go ahead and import next response from next server like that import get current user from add slash app actions get current user and import Prisma from add slash app slash lit slash Prisma DB like that great let's create an interface I parents with a listing ID question mark whoops string just like that and write export async function delete make sure you didn't accidentally write export default open this function inside and write request request and the structure params here and just map it to params by params like that and I'm just going to remove the space here so it's more semantically correct all right now I'm gonna get the current user so const current user is equal to await get current user like that if there is no current user and it's trying to delete the property we're gonna write return nextresponse dot error like that now let's extract the listing ID from the parents and let's actually check if the listing ID is correct so if there is no listing ID or if typo listing ID is not equal to string we're going to throw new error invalid ID like that great and now all I'm gonna do is const the listing is equal to 08 prisma.listing dot Elite many and doing that right where ID is equal to listing ID and user ID is equal to current user.id so the reason I'm using delete many and not delete one uh is because we cannot pass this user ID we cannot query by multiple aware inside the lead menu and I only want the current user who is also the owner of the listing which we can check by comparing with user ID to be able to delete this listing great and you can just go ahead and write return next response.json listing like that great and now we have the working route so this slash API Slash listings slash ID should actually work just fine I'm going to refresh my page just to ensure that there are no errors ready to happen great and I'm gonna go ahead and try and delete this Europe Sweden property here so I'm gonna click delete property and we get a success listing deleted and it's no longer here and if I go into my index page to try and see uh I can only see the creation one great great job we successfully finished all of the sidebar items the only things left now uh is to connect these category filters and create a model for this search uh filters right here and it's not going to take too much time because we created majority of those components using this rent model here and we're also going to add some loading States we're going to deploy it to versal and that is going to be it great job all right so now we're going to create the actual search model here so I'm going to close everything and let's go into Hooks and let's just copy uh use a login model and paste it and rename it to use search model like that let's rename all instances of login model into search model like that so we're going to have a search model store use a search model and Export default very important to use a search model great now that we have that we can go into app into components into navbar search right here and what we're actually going to write is we're going to use our search model so cons search model is equal to use search model like that and you can import that from ad slash apps hooks use search model great and now what we're actually going to write on this main div right here I'm going to add on click search model dot on open like that whoops so just on open do not execute it so just search model dot on open like that if you click nothing's gonna happen now so you don't have to click now and what I'm going to create next is I'm going to go into app into components into models and I'm going to create a new file search model that's the SX like that I'm gonna name it uh search model like that and what I'm going to do now I'm going to return a model component which you can import from dot slash model because we are in the models folder and we have the model right here great and I'm just going to add const search model is equal to use search model like that you can import that from add app hooks use search model like that and I'm going to add a couple of controls here so is open it's going to be controlled by search model that is open on close it's going to be search model dot on close on submit is going to be search model dot on open title uh is going to be let's see what the title should be so the title should be filters the action label is going to be submit or let's say search yeah like that great okay and now that we have this search model let's go into our app folder right here let's go into layout.the SX and the same way we added rent model let's just import search model like that from dot slash components models search model let's place them along here with all the other components great and now if you try and click on this uh make sure you refresh before that so just make sure the page compiles great and we get an error here and that is because I have not marked the uh components models search model as use client so make sure you add use client at the top right here I'm Gonna Save and refresh again to see if the error went away great the error goes away and let me try and click here and great so now our search filters actually open the filters model great now let's actually go ahead and let's write some functions for this model so I'm going to write honest router is equal to use router which you can import from next slash navigation like that we're also going to use search we're going to use params as well so go ahead and write const terms are equal use search params like that also from next slash navigation uh great now let's add the current step so cons step step is equal to use state which you can import from react and the default is going to be one of our enums so let's go ahead I'm just gonna order this a little bit like that let's write our enums here so I know sorry Adam steps we're gonna have three steps so location is zero date is one and info is two so those are all the steps we're gonna have and the first step is going to be steps dot location just like that great now I'm gonna add a state for our guest count so const guest count set yes account is equal to use State and by default it's going to be one and I'm gonna do the same for room count so set room count and the same for bathroom count and set bathroom count like that great and we're also going to create a state for date range so const the range set date range is going to be used state and in pointy brackets make sure you add range which you can import from react date range right here so just the structure range like that and just add that to the top as well great and the default value of this state is going to be start date new date and date new date and T is going to be selection like that correct okay and now let's go ahead and let's import our map so const map is going to be going to use memo is your memo and now it's going to return dynamic which you can import from next slash Dynamic and dynamic is going to import uh import dot dot slash map like that and I'm gonna pass in SSR post here and just don't forget to add the dependency array location like that at the end great so we already did this in one of the other components so basically we're going to import map using use memo which returns Dynamic which returns an import of map and immediately we pass an object to turn off server-side rendering for that and we're going to re-render map every time we change our location uh great and right now and one mistake we made this location doesn't actually exist here right now so just go ahead and write const location [Music] set location use state and now we have our location and just give it a pointy brackets here type of country select value which you can import from DOTA slash input slash country select right here if you cannot import it just make sure that you have this export type country select value in your components inputs country select right here we're going to use that in this search model okay great now that we have our map let's create our own back and on next and on submit values so I'm going to go ahead and write const on back use callback which you can import from react what that is going to do is it's going to say set step and it's going to turn the current step and just give it a value minus one so we're just going to reduce what currently exists const on next is going to be the opposite of that so use callback add the dependency array and just write set step value Value Plus one like that we already did this in the rent model but this is going to be a much simpler one than the rent model great and now we're going to write our uh on submit function here so go ahead and write const on submit is equal to use all that like this and just expand this function here and make sure the function inside callback is asynchronous synchronous like this great first things first we're going to check if we are on the last step so if step is not equal to steps dot info so if we are not on the last step in that case we're just going to return on next right back great otherwise we're going to define the current query select current query query is equal to an empty array like that if there are any parents where do we get the params from well from eu's search params right here so if we have any terms we're going to use current query and now we're going to parse those parameters so for that I'm going to import Qs and just go to the top right here and import us from query Dash string like that great go back into your unsubmit function and just write Qs dot uh Parts arms to string like that great and now just write const updated query give it a type of any and inside of that you're going to spread the current query so everything that currently exists which could be the category for example you're going to spread the current query and you're just going to write location value equation question mark dot value like that you're going to write a guest count room count oops discount room count bathroom count like that and that's going to be our initial updated query and now we're going to check whether we added and date ranges so we're going to write if date range dot start date in that case we're going to write updated query dot start date is equal to format ISO which you can import from date Dash FNS so just make sure you import format ISO from date Dash app and S I'm going to put it here to the top go back into your uh on submit function here all right so we're going to format ISO uh date range dot start date like that very simple we're going to do the same thing with end date so if date range dot end date we're gonna write updated query that end date is equal to format ISO and paid sorry date range Dot and date like that great so obviously we have to transform our start and end date into Strings because they're gonna go into our URL That's How we'll be able to update This Server component all right and now that that is done all we can do is create our final URL so const URL is equal to qs.stringify URL open objects inside and our URL is going to be just an empty slash like this and our query is going to be updated query which we modified above and just make sure you add the skip no true option in the end great after we've done that we're going to reset our step so we're going to write set step three steps dot location we're going to close the search model so search model dot on the close like that and we're gonna use router.push URL so this is actually going to apply all the filters make sure you have this router that push URL which pushes this stringified URL which uses this query which we will use regarding on all of our filters and make sure you add all of the dependencies it needs so that's going to be step search model location router guest account room count bathroom count make sure you don't misspell bathroom like I did so bathroom count date range on next and params like that great great job okay now let's create our action label and secondary action label for our next uh and back buttons so const action label is going to be used memo which you can import from react whoops like this don't forget the dependency array and we're going to write if step is equal to steps.info in that case we're going to return search because it's going to be our last step and the next click on the button is going to push the URL here great otherwise we're gonna return next because there is a Next Step coming and make sure you put step in the dependency array here great and now for the secondary action label we're going to do a similar function so const secondary action label use memo like this and inside of that we're gonna check if we are on the first step so if step is equal to steps dot location in that case we're going to return undefined because there is no step to go back on that point otherwise we're going to return back like that and make sure you put step in the dependency array of a secondary action label great and now I'm going to create our first body content great so go ahead and write let by the content it's important that it's left because it we're going to change it depending on the step like we did in the rent model great so open body content and write a div inside of that and let's give the class name and inside of that create a heading which you can import from dot dot slash heading so I'm just gonna order all of this here like that great second import heading from dot dot slash heading because we are in the components folder all right and this heading is a self-closing tab it's going to have a title of where do you wanna go like that and the subtitle of find the perfect location like that and just below that we're going to import our country select component which we already have created when we created the rent model so import country select from dot dot slash input slash country select you can find all of those here great I'm just going to put this uh with the other components here let's go ahead and let's actually add some values to it so initial value is going to be location which we have in our uh use state right here all right and on change we're going to have an arrow function which accepts a value and it's going to call uh set location is going to pass it the value as country select value like that great and just below countries select we're going to add an HR here and we're going to add our map I'm going to give it a prop of center of location question mark dot lat LNG like that great I'm going to use this body content now to pass it here so I'm going to write body bloody content like that and if you wait a couple of seconds for it to update and refresh you should see the new body content in the filters if it's not updating I'm just going to refresh manually to load the newest version all right so I just refreshed and there we go we have our first step and if I select Croatia for example you can see how fast we've developed this because we have all the components we need great and now we're going to create uh The Next Step which is date so go ahead and just below this body content created if step is equal to steps.day in that case study content is going to be another div with a class name of Plex black stash call gap-8 again inside of that it's going to have another heading component so heading like this it's going to have a title of when do you plan to go and the subtitle of make sure everyone is pretty like that great and just below that we're gonna add our calendar component which you can import from dot dot slash inputs slash calendar we already have it we created it uh so it's right here in components inputs it's calendar right here we already created it when we were creating our reservations grow rate and now that we have our calendar let's give it some options here so value is going to be date range and on change is going to be an error function which has a value and it's going to use the function set date range and pass in value dot selection so not just value but value dot selection like that perfect and before we move on uh I'm gonna modify uh this action label search uh to actually be the the dynamic action label like that great so now if we just wait a couple of seconds it's gonna say next here and if I click next uh oh yeah we did not we did we didn't add uh a proper unsubmit function here so instead of on submit uh search model on open modify the on submit here to call the actual on submit function which we created great so now just wait a couple of seconds for you to update try and click uh next again or again you can just refresh the entire thing but there you go I am on step two and I can see uh the date range but I don't have the back button so let's go ahead and let's just add that so I'm going to write secondary action to be Dynamics it's going to be step is equal to steps.location in that case it's going to be undefined otherwise on back like that so secondary action is going to check if the current step is steps.location and that case is going to throw undefined otherwise it's going to be on back so we don't want to display the secondary action if the current step is the first one great and make sure you also add secondary action label which is just the dynamic secondary action label constant that we created alongside action label so we have it right here great and there we go now we have our back button and I can go back and you can see how Croatia stays selected I can maybe choose India I can go forward again and you can see how now I am on my calendar great great job okay and now let's go into our last step which is info so I'm gonna go and create if step is equal to steps dot info like that in that case our body content is going to be a little bit different so create a div right here give it a class name Flex Flex Dash call Gap Dash 8 like that give it a heading again like that give it a title of more information and a subtitle of find your perfect place like that great and now we're gonna add our counter component which we also have and you can import counter from dot dot slash input slash counter so the same way we did with country select calendar and counter great I'm going to go here to the bottom and let's give some props to this counter here so uh the title is going to be yes the subtitle is going to be how many guests are coming like that the value is going to be guest count and the on Change is Gonna Be a narrow function which passes the uh guest count value to set yes count value just like that great great job I'm going to go ahead and click next here great so we now have the option to select how many guests we are passing with us let's just copy this counter two more times for the second one we're gonna write rooms and how many uh rooms do you need and instead of a guest count and set guess down here we're going to use Rune count and here we're gonna write set room down like that and for the last one it's going to be bathrooms so it's gonna ask how many bathrooms do you need instead of gas counts gonna be bathroom count and instead of set guest count is going to need set bathroom down like that great great job let's just wait a second for this to refresh great and now we have it right here and you can see how I can individually control all of these values great and I'm going to select a date right here for example and I'm gonna select uh grease here right you can see how everything stays selected and once I click on search if you take a close look in our URL you can see that it just added a bunch of filters which is exactly uh what we want so we're going to use these filters uh to send it to our server component and you can also notice how it also has our category because I have selected windmills if you don't that's completely okay you can try and click on Countryside or islands and your category should just be added here maybe it's added at the end don't worry but it should not reset your entire URL great and now let's actually modify our get listings action so I'm going to close everything here and I'm going to app actions get listings again let's actually modify this now so it takes in consideration every single of the parameters which we just passed and gives us the perfect result that we want great so in this eye listings parents where we just added our user ID let's also add yes count which is an optional number let's add account which is also an optional number let's add bathroom account which is also an optional number here let's add the start date which is an optional string and date it is also an optional string location value which is an optional string and category which is an optional string so everything is optional because we don't have to select anything here great and now instead of just extracting user ID let's collapse this and extract all of our values so user ID room count guest count bathroom count location value start date and date and category like that so let's just make sure we have user date guest count room count math room count start date end date location value and category great great great job all right and now we're going to use the same principle we did with this user ready to create all kinds of combinations so just below user ID go ahead and write if category so if there is a category we're going to write query.category to be equal to category like that below category you're going to write if roon count from count we're gonna write query dot Rune count open an object inside and we're going to write GTE which means greater than or equal and just add a plus Rune count so it's important that you add this plus right here because that is going to transform this room count which is initially a type of string when we send it uh into a definite number great and we're gonna query all these things which have either the equal amount of room count that the user sent or more than that but we're gonna filter out everything that has less rooms that we need so we actually get the result that we need great and you can copy this and we're going to reuse it the same way uh for bathroom count and guest count as well so go ahead and write uh sorry guest count and write query dot yes count and the same thing guess count right here great now uh here do the same thing for bathroom account if query.bathroom count just write bathroom count like that great so what's important is that you have to use this GTE and that you put a plus in front of every of these values right here great and now that we have our bathrooms count let's add our location filter so if location value in that case we're going to add query dot location value is equal to location value like that and now we're going to create the filter for our date range so we are going to filter out all listings which have a reservation in our desired date range so this is going to be the most complicated filter but it's not that hard to understand let's go ahead and write if start date and end date so we need to have both we're going to write query dot not so we are going to use reverse filtering on this so we uh we're going to write a filter that actually finds all of this uh listings inside this date range but by using query.not we can reverse that logic so go ahead and open an object on query.not and write reservations like that inside of that open an object and write sum inside of that open another object and write capital or inside of that you're going to open an array like this and first let's define the first combination which is going to be end date open an object here and write gpe start date and below that write start date LTE start date like that and below this create another object in this or array right here and write start date LTE end date and end date open an object and write GTE and date like that so basically what we do here is we use these two combinations to ensure that we filter out all kinds of uh conflicts in reservations so uh if there is a single day in the reservation date range we are going to filter out that listing because we cannot create a full booking on that so you can play around with this logic but I find this to be the most useful one to filter out our dates great and you can save this and that is automatically going to be added here in the query right here and you can see how my listing updated it because all of this uh all of these queries have now been required and I can click remove all filters and that is going to redirect me here great so in order to make this a little bit work a little bit better uh you can you can actually go ahead and try right now so let's instead of Greece let's just refresh everything so refresh that you are on the blank page here all right and now we have one listing in Croatia you might have it somewhere else so go ahead and try and select for example Aruba I'm going to click next next and search and I have nothing in here but if I go and change Aruba to Croatia and click on next next next you can see that the Croatia appears because our search filter is working now go ahead and I'm going to add a reservation to uh this creation property here and I'm going to see if I can filter it out using the new filters so just click on this reservation wait for sorry for on this listing uh wait a moment for it to compile all right and what I'm going to select here I'm going to select so April from 9th to 15th I'm going to reserve this whole week right here so I'm gonna go in April 9 to 15 and Reserve an entire week here let's just wait a second for this to complete great so I created this booking from 9th to 15th I should be redirected to slash trips now great so I have this reservation here and let's go back into our home page here and I'm gonna go and select Croatia again but what I'm gonna do is I'm gonna create I'm gonna search that uh inside of this you know we know that we created a reservation in this week so I'm gonna go ahead and try and select from 10th to 14th for example so something inside of that I'm going to click next and search and you can see I have no matches for that because we filtered out Croatia because we just created a reservation for that but if you go into my trips for example and cancel the reservation here and now go back into Airbnb here and I'm going to select Croatia again and I'm going to select this one more time and click search now you can see that now we find this reservation amazing amazing job you finished the complicated part you finished uh uh the actual search filters here and I'm just going to create a couple of more of this listings so we can actually see how this looks better great so I created three listings here one is Arctic one is modern and one is I'm gonna go and refresh all of my filters here and let's try and query but by our categories so I'm gonna click on Beach and you can see how that only shows Croatia because only Croatia has the beach property and we do that using this uh where is it category filter right here if I try and click on Modern you can see that only Greece shows up and if we try and click on Arctic you can see that only uh Antarctica shows up great great job and let's try and let's uh filter something else so let's see how many uh this has three rooms right here so let's go and let's filter by rooms for example I'm gonna go here it doesn't matter where we go uh doesn't matter when we go but I need a minimum of two rooms and you can see how it filtered out the place which doesn't have two rooms so only Antarctic and Croatia have a minimum uh which they have three rooms but that is more than we need but enough to not filter that out but if I uh reset my filters you can see that Europe Greece has only one room and that's why it was removed great great job so you finished the pretty much the entire application uh what's left now is to add the loaders so I'm gonna go ahead and do that now so if you open your project right here and if you go into app uh you might have heard that we have a couple of new reserved names which you can use either in the root or in individual Pages like here so alongside page.tsx and layout does TSX we also have error and loading.dsx so first I want to create a loading one so go into app and create a new file called loading.dsx right here and inside of that I'm going to import loader which we actually don't have created yet so let's just write it like this for now let's say const loading and just return a div which says loading Three Dots and make sure you export people loading like that and now if you refresh this error is going to go away uh because we uh export the default great and you can see now I have for some time I have this loading component right here uh great and if you're by any chance stuck on the loading page you can just hit refresh so it loads everything again now instead of this loading text I want to create an actual loader so let's go ahead and let's go into our components and let's create a new file called loader loader.tsx like that great let's mark this as use client component and first things first let's install a package so I'm going to shut down my session here and write npm install react Dash Spinners like that let's wait a second for this to install all right and I'm going to import puff loader from react Spinners and feel free to run npm run that again just make sure that you refresh your application great and in this loader I'm going to write const loader don't forget to ask for default loader at the end right here and all I'm going to write is return div and I'm gonna be I'm gonna give it a class name of H dash 70 VH Flex Flex slash call justify Dash Center item slash Center like that and inside of that I'm going to use the pop loader which we just imported which is a self closing tag right here and I'm going to write a size I'm going to give it a size of 100 and color of red like that and I'm Gonna Save this file and let's just wait uh for a second for this to refresh properly great and now we can go ahead and try and click on some of your favorites some of your reservations and for me it loads pretty fast but there is a chance that you can catch it loading somewhere uh you can maybe reduce your um CPU power in the network tab uh but I'm just going to click around until I can catch it in action so I'm going to refresh one more time and just to make sure it's here oh yeah it's actually not here because we have to go back into my bed I forgot we have to go back into loading the TSX right here and replace this uh div loading with an actual loader I'm sorry I completely forgot so you can write loading like this and just import it sorry loader and import it from dot slash components uh Slash loader like that go ahead and save that and now I can try okay wait for the page to refresh again all right and let's see if we will be able to catch it here if it loads too fast of course you're not gonna see it but uh go ahead and try and click on these categories okay we cannot see it here let's try on reservations and did you see it we have a little owner I know it's hard to see but yeah uh basically the more data you add here or try and click on a single listing and you will be able to see more and more loaders so I didn't have enough data for this loaders to display but uh if you play around if you go into your trips your reservations and your properties you're going to be able to catch a blink of that loader so right now I'm trying to click here let's try and go back maybe yeah you can see how it was loading for a second there uh great great job and now I want to do the very same thing for the error page right so let's close everything up here and inside app folder create a new file error that's the SX and just mark this as use client and import use effect from react like that you can see how it already detects something so go ahead and write an interface interface error State props give it a prop error which is a type of error like that and write const error state which is a react functional component which accepts error Spade props like that and just destructure the error from there and right completely Arrow function like here and just remember to export default error State like that great and inside the error State you want to write a use effect like this and what you're going to do is you're gonna add the error in the dependency array and just write console.log error like that and instead of console.log you can actually use console.error or you can use your analytics to send it somewhere and we're going to return empty state which you can import from dot slash components slash empty state right here and just pass in the title and a subtitle of something went wrong like that great and if you really want to test out this error State uh you can go ahead and go into app into page that's the SX and right here what I'm going to do is I'm just gonna throw new error something went wrong like this so I'm going to purposely throw an error to see if our page is going to catch that so I'm just going to refresh everything here all right and you can see our loader in action here and there you go so this is the loader now let's just wait a moment and there we go we have our uh error page right here and we have one error right here so we handle the error with the page great and make sure you just remove this throw a new error here all right so we have two things left to do here and one of them is when we apply a search filter for example Croatia right here and I select let's say uh three days like this and add five guests we want that to appear in this search bar right here at the top so let's go ahead and do that let's go into our components again and let's go into navbar into search.dsx in just above our search model let's actually add const params equal to use search params from next slash navigation like that and make sure you execute the hook and let's also import our get by value from use countries hook which you can import from add slash app slash hooks use countries which we created in one of the previous parts now let's get individual fields which we will need in this search component from the parameters so cons location value is equal to params question mark get location value you can just copy this a couple of times we're going to do the frame for start date so go ahead and write start date Paramus get start date and date forums get end date and guest count forums get yes count like that now let's define our location label so const location table is equal to use memo which you can import from react right here I'm going to put it to the top and just open a function inside don't forget the dependency array and its location value is present in our parameters for getting the return yet by value uh location value as string question mark dot label like that otherwise return anywhere like that so just make sure you return inside this if close and return uh yeah anywhere like that and fill the dependency array with get by value and location uh value like that great now let's write our duration label so consideration label is equal to use memo as well I'm just gonna scroll a bit like this plus duration label is checking for start date and end date and if they are both available first Define start so new date start date as string const and new date and date as string like that and just create a let div equals the difference in days which you can import from date slash FNS like here I'm going to put it to the top and you're going to pass in the end and start like that great and you're just gonna add uh one check here so if this is equal to zero in that case the is equal to one like that and just return open template little strings like this and write this special object with days like that whoops so this days like that and outside this it closed just right return any week like that and fill the dependency array we start date and end date like that and uh now we just have to write the guest label so cons cast label is equal to use memo as well and it gets count in that case return template literal strings and inside just right uh guest count yes like that otherwise return add guests like this and you just have to pass in the guest account like that great right now we're gonna replace this hard-coded labels here so instead of anywhere it's going to be location label so replace this anywhere right here with location label uh like that and you can see how it changed to Croatia because Croatia is set in the URLs and a week is going to be replaced with duration label like that you can see how it now says two days and add guests is going to be replaced with guess label sorry guest label like that great you can save that and there we go we have Croatia two days four yes if I change this for example uh to India and let's select another thing let's add more guests search and you can see how it modifies that as well great and one thing I want to bring to your attention is if we log out for example great and if I try and manually go to slash favorites and you can see how it actually says no favorites font here even though I'm not logged in so we don't want that to happen so let's go ahead and create a new file uh not in app not in components no just in the root of your entire project create a new file middleware.ts like that and inside that middleware.ds you're going to export default from next dash out slash sorry next dash out slash middleware like that and just export const config matcher open an array and inside we're going to protect all of our routes which need to be protected and those routes are slash trips go ahead and copy this a couple of times so slash trips slash reservations slash properties and slash favorites like this go ahead and shut down the application and start it again I'm just gonna go back to the root like this let's wait a second for all of this to compile and let's try and manually go to localhost 3000 slash favorites while we are logged out and see what happens great you can see that we are reverted back to slash and we have a callback back to favorites so we cannot be logged out and access our favorites but if I log in foreign go to my favorites for example there should be no problem just like that great great job you finish the entire project all that's left is to deploy on reversal alright and in order to deploy this on Verso what we have to do is you have to push this current repository you have into your GitHub account so I have this video Airbnb repository in which I was building this and in which I am recording right now so all I run was npm run lint to make sure that there are no errors in our page so warnings are okay but make sure you fix any errors that you might have and just push this into the master Branch right here and now I'm gonna go into my virtual I'm going to click add new to my account right here I'm going to select video Airbnb and what I have to do is I have to add some environment variables here so for that you can go into your dot environment file right here and if you want you can just copy everything like this and you can just paste it in here and it's going to populate everything just like that great and after that you can just go ahead and click uh deploy but I'm going to change the name of this so don't use Airbnb in your names I'm just going to change this to a video uh rent example don't use Airbnb in the URL main because you might get a flagged for phishing and just click deploy and just like that we successfully deployed our project you're gonna get a screen similar to this uh let's just wait so it assigns everything great so you can see it loaded successfully and now I have it right here you can see everything loads I have the URL uh I can individually click on it and see how it's much faster in production that's great and you can actually see the loading States you see it's lightning fast in production changing anything is very very fast here you can see how we have our login models if we try to like we have the login model uh you can go and search anything it's going to work amazing amazing job and one thing I want you to notice that if once you get this deployed on Virtual you're gonna need to change uh your all out uh URLs right here so you can no longer use localhost right here instead you're gonna have to use whatever is uh in the uh in your url that was assigned so in my case uh it's this so I can just update the application and you also have to go back into um Google Cloud right here and just change that as well like that and save that to ensure that your all outs are working great great job thank you so much for watching this video please remember to leave a like share and subscribe and comment if you like this video and see you in the next one
Info
Channel: Code With Antonio
Views: 853,101
Rating: undefined out of 5
Keywords:
Id: c_-b_isI4vg
Channel Id: undefined
Length: 520min 33sec (31233 seconds)
Published: Fri Mar 31 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.