0:00 Hey, what's going on guys? So, we're 0:02 going to do one of the larger projects 0:03 that I've done on this channel. It's a 0:05 4-hour long project, and we're going to 0:07 build a booking app for uh for meeting 0:10 rooms or conference rooms for companies. 0:13 And we're going to be using Nex.js along 0:16 with AppRight, which is uh just an all 0:19 around platform for databases, for 0:21 authentication, 0:23 for uh file storage. We'll be storing 0:25 our images for the the rooms that we 0:27 that we're booking. Um, so there's going 0:30 to be quite a bit that goes into this 0:31 and we're going to be using Nex.js 0:33 actions as well. So, AppPright is 0:36 sponsoring this video. Um, there is a 0:39 very generous free tier so you could 0:41 follow along. You don't have to pay for 0:43 anything. You don't have to put in any 0:44 credit card information. You can create 0:46 your databases, your storage buckets and 0:48 all that. Um, so I would suggest 0:51 following along and we'll be able to 0:54 authenticate. We'll be able to add rooms 0:56 to rent out. will be able to book rooms. 0:59 We're going to add protection so that 1:00 you can't double book a room for the 1:02 same time frame. So, there's going to be 1:04 quite a bit um that goes into this 1:06 project. It's going to take a while. So, 1:08 you could just do it, you know, in 1:10 increments and it might take you a few 1:12 days. It might take you a few weeks 1:14 depending on your schedule. But, I think 1:16 you're going to learn a lot from it when 1:17 it comes to React, when it comes to 1:19 Nex.js, when it comes to AppRight. So, 1:22 that's it. Let's go ahead and jump into 1:24 it. 1:30 All right, guys. So, before we do 1:31 anything, I want to give you a quick 1:32 demo just so you know what you're 1:34 building and what to look forward to. 1:36 All right. So, and we're going to start 1:38 just by creating some of the UI like the 1:40 navbar, um the the rooms here. We're 1:43 going to use some static data at first 1:45 in a JSON file, and then we're going to 1:47 create our app database, create our 1:49 storage buckets, install the SDK, 1:52 implement authentication, and all that. 1:54 So on the homepage, we list all the 1:56 available rooms. You can see there's an 1:58 image. There's um just a name, address, 2:01 availability, and price. Click view 2:03 room. Shows us some more info. And we 2:06 have the booking form. Now, if I try and 2:09 book a room now, notice I'm I'm not 2:12 logged in. So, it's just going to 2:13 redirect me to the login. So, let's 2:16 quickly register a new account. So, just 2:19 put whatever in here. And password. And 2:23 again, authenticate, all authentication, 2:25 everything is through appreite. There's 2:27 no uh no other libraries or anything 2:29 like that. So, I'm going to log in. 2:31 Bradgmail. 2:37 All right. So, now I'm logged in. I have 2:38 some some new options up here. So, my 2:41 rooms, if I add any rooms to to rent 2:43 out, those will be shown here. If I book 2:46 any rooms, those will be shown here 2:48 under bookings. So, let's quickly just 2:50 add a room. I'm just going to use my 2:52 form filler here and I'll change the 2:54 name though to my room and we can upload 2:58 an image and again this is all through 3:00 through apprite the storage buckets. Go 3:02 ahead and save that. 3:05 So room created successfully and you can 3:07 see my room right here. Click view room 3:10 takes us and now it can be booked. All 3:12 right. So that's how we can add rooms. 3:14 Now let's say we want to book a room 3:15 like this training room. And now that 3:17 I'm logged in I should be able to do it. 3:19 So, let's book it for tomorrow. Let's 3:22 say we want it at um we'll just do 1210 3:26 12:10 p.m. to let's say 2:10 p.m. All 3:30 right. So, I'll go ahead and book that. 3:33 Takes me to my bookings page where it 3:35 shows all your bookings and the check-in 3:37 and checkout. You can cancel it from 3:38 here as well. And we're also going to 3:41 write the logic so that if it's already 3:43 booked between a certain time, you're 3:45 not going to be able to to book another, 3:47 you know, make another booking in that 3:49 time. So, for example, if I were to try 3:53 this again 3:55 and I did what 1210 to 210. So, let's 3:58 say we want to do one 111 to whatever 4:02 311. So, that shouldn't work because 4:05 it's within the time frame of the other 4:07 booking. So it says right here, this 4:09 room is already booked for the selected 4:10 time. So if I go and I do what did we 4:14 end it at? 3:10. So we can do like if we 4:17 started at 3:11 and go to 4:21 whatever 611. 4:24 That should work. Okay. So now that gets 4:26 booked because it doesn't interfere with 4:28 this time range. And of course I can 4:30 cancel the booking. 4:33 So that's pretty much the gist of of 4:35 what we'll be building. All right. So, 4:37 yeah, we can sign out. 4:40 So, let's get started. And also, I'm 4:42 going to put the link to the repository 4:44 in the description, and it has the full 4:47 app as well as the theme files, which is 4:49 all the HTML with all the Tailwind 4:51 classes. So, we we will be grabbing a 4:54 little bit of the the markup and classes 4:56 just so we don't have to type out a 4:58 million classes. All right, so let's get 5:00 started. I'm going to open up my 5:02 terminal. And you want to just go to 5:04 wherever you want to create this. And of 5:06 course you need Node.js installed 5:08 because we're going to run npx. And then 5:10 we want to do create d-next- 5:14 app at latest. And then I'm going to 5:17 call this book it. 5:20 And then we'll go through and answer 5:22 these questions. So TypeScript, I'm 5:23 going to say no. Of course, if you want 5:25 to use TypeScript, you can or ESLint. 5:27 I'm going to say no to those and yes to 5:29 Tailwind, no to the source directory, 5:32 yes to the app router, and no to the 5:34 customizing the the alias. So that's 5:38 going to set us an application with 5:40 Nex.js, ReactDOM, and React, as well as 5:43 the dev dependencies of Tailwind and 5:45 post CSS. 5:47 So now I'm going to open up VS Code in 5:50 that booket directory that I just 5:52 created 5:54 and I'm going to open my integrated 5:56 terminal because I'll be using that from 5:57 now on. And we'll just go ahead and run 5:59 the server. So npm rundev 6:03 that's going to run on localhost 3000. 6:06 Just going to uh I guess I'll leave I 6:08 can leave this open the the final 6:11 version, but let's move this over here. 6:14 All right. So now we have just the the 6:16 landing page. the Next.js landing page. 6:18 And I just want to let everyone know 6:21 that this is not like a a crash course. 6:24 You should know Next.js, at least the 6:26 very basics, how to create a page or a 6:28 component, uh how filebased routing 6:31 works. I'm not going to explain that. If 6:33 if you're brand new to Nex.js, then go 6:36 ahead and watch my Nex.js crash course 6:38 before you watch this. 6:40 So, what we're going to do is open up 6:42 the in the app folder. The page.js JS is 6:46 the homepage right here. Um, so I'm 6:48 going to just kind of clear this out. 6:50 Get rid of that image import. And then 6:53 everything within the the div I'm going 6:55 to get rid of 6:58 and get rid of all these classes. In 7:00 fact, we don't even need a div. It'll 7:01 just be a a fragment. And then I'll just 7:04 put an H1 for now and just say book it 7:06 app. 7:09 Okay. And then for the styling, um, this 7:12 global CSS, we're using Tailwind, so we 7:15 want these directives, but everything 7:17 under that we can get rid of. So just 7:19 delete that. And if you want to keep the 7:21 global CSS in the app folder, you can. 7:24 But what I do is I like to create a 7:26 folder outside in the root called 7:28 assets. And then in assets have another 7:30 folder called styles and then move the 7:33 global CSS into styles. Now, if I do 7:37 that, it's going to break because in the 7:39 layout where the CSS is being brought 7:41 in, it's not being brought in from the 7:43 right place. So, I just want to change 7:45 that now. Oops. I want to change that to 7:49 at slash and then assets slashstyles 7:56 globals.css. 7:58 And that should fix it. All right. Now, 8:01 there's a couple more things I want to 8:02 do in this layout file. So they're now 8:05 using this gist or jist I don't even 8:08 know how to say this this font and I 8:10 want to use the inter font which is what 8:12 they used to use. So I'm going to just 8:14 instead of importing local font I'm 8:16 going to import enter uppercase I and 8:19 that's going to be from next font google 8:22 / google and then we can get rid of 8:24 these two right here and create a new 8:27 variable called enter 8:30 uh yeah lowercase enter and set that 8:32 equal to uppercase enter and we're just 8:36 going to pass in here let's say subsets 8:40 set that to an array with a string of 8:43 Latin. 8:45 Okay. And then I want to use that font 8:47 globally or in the body here. So for the 8:50 class name, we're going to set it to 8:53 enter do.class name. 8:56 And that should change the font to 8:58 enter. All right. Now, um the metadata, 9:01 I'm just going to change this. You can 9:03 put whatever you want here, but I'm just 9:05 going to say book it app. 9:07 And I'll just say book 9:10 book a room. And then for the 9:13 description, 9:15 uh, I'll just do 9:18 book a meeting or 9:21 conference room 9:23 for your team. 9:26 All right. And then the last thing I 9:27 want to do here for now is the children. 9:29 This is where, you know, all the page 9:31 content is output. I just want to wrap 9:34 that in a main tag with some classes. So 9:37 right here, let's say main. And I'm just 9:39 going to add uh a couple classes. So 9:42 let's do MX auto. So margin auto on the 9:45 X axis. Going to do a max width of 7. 9:49 We'll do 7 XL. Padding on the Xaxis 4. 9:53 Padding on the Y ais 6. And then I'm 9:58 also going to add on small screens and 10:00 up. So SM colon PX6. 10:04 And then on large so lg colon going to 10:07 do px8. 10:09 All right. And then in the main tag is 10:11 where we want to move the children prop. 10:15 All right. So that'll just kind of give 10:16 it a container. 10:19 So now let's create our first UI 10:22 component which is going to be the 10:23 header. So in the root we're going to 10:25 create a new folder called components. 10:29 And then in components let's create a 10:31 file called header.jsx. JSX. Now, I like 10:34 to use the JSX extension. So, I'm 10:36 actually going to change both of these 10:38 as well. So, layout, we'll change that 10:40 to JSX, as well as the page. Change that 10:43 as well. And that should still work just 10:45 fine. 10:47 Okay. Just reload. That should Yep. So 10:50 for the header, I'm using the VS Code 10:52 extension react simple React snippets 10:55 where I can just do sfc enter and it'll 10:58 give me an arrow function with the 10:59 export at the bottom which is like how I 11:01 like to format my components. And then 11:03 we'll just call it header. Oops. And you 11:06 can just kind of tab into the props 11:08 which this doesn't have any. And then 11:09 tab into the return. And for now let's 11:13 just put in we'll just say header and 11:16 just get it on the page. Now, I want 11:18 this on every page, so I'm going to put 11:19 it in the layout. So, in layout, let's 11:22 go ahead and import header. And then 11:25 let's go down right above the main tag. 11:28 And we're going to embed the header 11:31 component. And now you should see the 11:32 the header text. Now, as far as the what 11:36 we want to put in the header, there's 11:38 quite a bit of HTML and and Tailwind 11:40 classes. So, what I would suggest doing 11:42 is cloning or downloading the repository 11:46 and then taking the theme files which 11:48 are right here underscore theme files 11:50 and just putting them in the root of 11:52 your project. Just copy the folder. If 11:54 you do that, then you'll have easy 11:56 access to the HTML. All right. So, I'm 11:59 going to open up the index html and what 12:02 we want is the entire header tag and 12:04 everything in it. So, I'm just going to 12:06 grab this the mobile menu as well. So 12:11 everything in the header and then we're 12:13 going to just put here some parentheses 12:15 and then we can paste that in. Now I 12:17 have some HTML comments in here to kind 12:19 of break things up. So you're going to 12:22 have to just either delete them or if 12:25 you want to keep them, which I'm going 12:26 to then just use you just want to use a 12:28 JSX comment by doing command or control 12:31 forward slash to comment it out like 12:33 that. I believe there's five. So that's 12:35 one, two, 12:37 and then this one. three, 12:40 four, 12:43 and five. I think that's all of them. 12:46 Yeah. So, we save that. You should see 12:48 it. Now, we obviously we want to change 12:51 class to class name. So, if you 12:53 highlight one and then just do uh 12:55 command or control shift L, that'll 12:58 select them all. And then we'll change 13:00 all of them to class name. 13:03 All right. Now, the way that this this 13:04 menu works is on large screens, you have 13:07 your, you know, your left and right 13:09 side. We'll fix the logo in a minute, 13:11 but on smaller screens, the left side is 13:13 going to get knocked down like that. 13:16 Okay. So, there's no hamburger button or 13:18 anything. Um, and this does look 13:20 cluttered because it shows every link, 13:22 but in reality, when we're, you know, 13:24 later on with authentication, login and 13:27 register are only going to show when 13:28 we're not logged in. My rooms and sign 13:31 out are only going to show when we are 13:32 logged in. These two will also be when 13:35 we're logged in, so it's not going to 13:37 look as cluttered later on. All right, 13:40 so let's take care of um the logo next. 13:43 Now, if you look in the theme files, 13:45 there's an images folder and there's a 13:46 logo SVG. So, what I'm going to do is in 13:49 assets, the assets folder that we 13:51 created, I'm going to create another 13:52 folder in there called images. And then 13:55 move or not move, but copy the logo from 13:58 here into assets images. And then we 14:02 what we can do is bring that in to the 14:05 the header. And we're going to bring in 14:07 a couple other things as well. So, we 14:09 want the image component from next. And 14:12 we also want the link component. So, we 14:15 might as well bring those in now. and 14:18 then bring in the logo itself. So let's 14:20 say import logo and that's going to be 14:23 from 14:24 at slashassets slashim images 14:28 slashlogo.svg. 14:32 Okay. And then we'll go ahead where down 14:35 here where we have the image tag replace 14:38 that with image like that and change the 14:42 source 14:43 to the logo variable. So, source equals 14:47 brackets logo. And we just also want to 14:51 add here priority 14:54 and set that to true. 14:57 All right. So, if we do that now, you 14:59 should see the logo. 15:01 All right. Now, next thing, we brought 15:03 this link in and that's going to replace 15:05 all these a tags. So, there's there's 15:08 quite a few here. You can go one by one 15:10 if you want, but I'm going to just put a 15:12 cursor in front of each one. I'm going 15:14 to hold down option or alt on Windows. 15:16 Click here. So, wherever I want to put 15:18 the cursor, this one, this one, 15:24 uh, this one. So, all the a tags right 15:28 here. 15:30 We get the mobile menu. So, here, here, 15:33 and here, I think is the last one. And 15:36 then I'm just going to delete backspace 15:38 and then put in link. And that also 15:40 replaced the ending tag. So, make sure 15:42 that that's formatted correctly. All 15:45 right. So, save that. Everything should 15:47 still work fine. If you open up the 15:50 console, we shouldn't see any weird 15:52 errors or anything. All right. Now, the 15:55 last thing I want to do for now in the 15:56 in the header is the icons because these 15:59 links up here, if we look in the final 16:01 project, you'll see they have these 16:02 icons. So, um, what I did in the theme 16:07 files is I just used the CDN for font 16:10 awesome. trying to find one of them and 16:13 then just added these eye tags with the 16:15 classes. But with React, I use uh a 16:19 package called React icons. So, I'm 16:21 going to open up a new uh new terminal. 16:23 I'm going to leave the other one, leave 16:25 the server running in that one. And 16:26 let's say npm install react- 16:30 icons. 16:32 Okay. So now what we can do is bring in 16:35 the icons as components. So let's say 16:38 import and this is these are all going 16:40 to be from 16:42 react icons slashfa because it's the 16:45 font awesome library because there's 16:47 other libraries you can use as well and 16:49 the icons I want are going to be fa user 16:53 we want fa signin alt fa sign out alt 17:00 and fa building. 17:04 Those are the four icons we want. And 17:05 then we're just going to replace the I 17:07 tags. I'm just going to search for angle 17:10 bracket I. 17:12 And let's see. 17:15 So right here, I'm going to change this 17:18 or or just get rid of this whole I tag. 17:20 That's the sign in. So that's going to 17:22 be FA 17:24 signin alt. And then I'm just going to 17:26 add two classes. So class name. And I 17:29 want it to be uh inline and margin right 17:33 one just to add some space. So, if I 17:35 save that now, we should see the little 17:37 login icon. 17:39 All right. And then what I'll do is copy 17:41 that and go down to the next one, which 17:45 is register. Replace this I tag. And 17:48 this is going to be the user icon. So, 17:51 FA user uppercase U. Oops. So, if we 17:56 save that. There we go. And then we have 17:58 the login one or sorry, log out. 18:02 So that's going to be sign out 18:05 alt and then the building one which is 18:08 going to go oh right here. So my rooms 18:12 replace that with 18:14 fa 18:18 building. 18:19 So now you should have those four icons 18:22 and that's all I want to do right now 18:23 for uh you know for this for the header. 18:26 Later on when we have authentication 18:28 we'll be doing some other stuff but we 18:30 can close that up for now. And then I'm 18:33 also going to add a footer. So let's 18:35 create a component called footer.jsx. 18:38 And I'm going to do sfc 18:41 footer. And I'm just going to grab from 18:43 the index html. 18:47 If we go down to the bottom, there's a 18:48 very very simple footer. So I'll just 18:50 grab this 18:52 and we'll put that right in here. Okay. 18:56 And then for the for the year though, 18:57 I'm just going to I want that to be 18:59 dynamic. So I'm going to say const 19:01 current year and let's set that to new 19:05 date and we'll use the get full year 19:09 method and then just replace 2024 with 19:14 brackets and current year so that way it 19:16 changes on its own and then yeah we'll 19:20 do that and then in the lay uh layout 19:24 yeah layout jsx 19:26 we're going to bring in footer so port 19:29 footer. 19:31 And we're going to put that right below 19:33 the main tag. So 19:38 footer. 19:40 And I mean, you can leave the background 19:42 gray if you want, but I think I'm just 19:44 going to get rid of that class and just 19:45 have it be just white like that. All 19:49 right, cool. So, we got the header and 19:50 the footer. We can close the layout. 19:53 Now, the next thing is the the rooms on 19:57 the homepage. And like I said, I'm not 19:58 going to dive right into using appite 20:01 because I just want to style it first 20:03 and then we can integrate appite and we 20:05 don't have to worry about all the 20:07 classes and stuff. So if you look in the 20:10 repository, 20:11 there's a data folder and there's a a 20:14 rooms.json file. So what I'm going to do 20:17 is bring that data folder into the root 20:20 of my application. So data, bring that 20:24 over. I'm just going to copy folder. 20:27 Okay. And then we can what we can do is 20:30 bring that in to the um to the homepage. 20:34 So let's go up at the top here and let's 20:37 say import and say rooms and that's 20:41 going to be from let's say at slashdata 20:45 slash and then rooms 20:48 rooms.json. 20:51 All right. Now I have access to those 20:52 rooms. So, I'm going to get rid of this 20:54 H1 and open up some curly braces and 20:58 let's add uh I'm going to check to see 21:01 if there are rooms. So, let's say if the 21:03 room's length is greater than zero then 21:08 parenthesis else parenthesis and in the 21:12 first parenthesis is if there are rooms. 21:13 So if there are, then I'm going to take 21:15 those rooms and I'm going to use map, 21:17 pass in our function here, and say for 21:20 each room I want to show. For now, we'll 21:24 just do an H3. Ultimately, it's going to 21:26 be a card, but for now, we'll just do an 21:28 H3. And just to make sure we're getting 21:30 the data, let's say room.name. 21:33 And then down here in the else, if 21:34 there's no rooms, then we'll just put a 21:36 paragraph and say no rooms 21:39 uh available 21:42 at the moment. 21:44 All right. So, let's check that out. 21:48 Yeah, right here. So, it's just showing 21:50 us the names of the rooms in the JSON 21:52 file. And just to take a quick look at 21:54 that file, there's an ID or dollar sign 21:57 ID because that's how it's formatted in 21:59 AppRight. And then a user ID because we 22:01 will have a relationship between a user 22:04 and uh room collection. And then the 22:07 name, description, square feet, 22:08 capacity, location, which is just like 22:11 where on the compound or in the building 22:13 it is, and then the address is the full 22:15 address, amenities, which is a 22:18 commaepparated list, availability, price 22:20 per hour, and image. And ultimately, the 22:24 image URL will be it'll come from 22:26 apprite, but for now, it's just going to 22:28 be static images. 22:30 All right, so we have that going on. Now 22:33 let's create our room card component 22:35 that we want to show for every room. 22:37 Right? So for that I'm going to go into 22:40 components create a new file called room 22:44 d uh uh room card.jsx. 22:49 And in here we want to create a 22:50 component called room card. And then as 22:54 a prop we want to pass in a dstructure 22:56 room 22:59 just room. And then as far as what I 23:01 want to return, it's going to be some 23:04 HTML. So again, I'm going to go back 23:06 into the theme files index and go up to 23:10 where we have the rooms. And I labeled 23:12 them. So we have room one. So right 23:14 under that comment, this div, which ends 23:18 right here above room two. I'm going to 23:20 grab that. So I tried to make these 23:22 theme files really easy for you to just 23:24 grab things from. So let's put that 23:27 right in here. And then of course uh are 23:30 there any comments? I don't think 23:31 there's any comments, but we need to 23:33 change the classes. So command or 23:35 control shift L change that all those to 23:39 class name. Okay. Now I'm just going to 23:42 save it as is with the hard-coded data 23:44 for now just to bring it in to our uh 23:47 homepage. So on the homepage, let's 23:51 import room card. And then instead of 23:55 this H3 right here, we're going to 23:56 replace that with 23:59 um 24:01 with room card and we're going to pass 24:03 in a room prop and pass in room. 24:08 All right. So, if I save that, now we're 24:10 going to have basically just this static 24:13 data for for every room that is in that 24:15 JSON file. 24:17 So, let's start to make this dynamic. 24:20 So, back in room card, a couple things 24:22 we want to bring in. So, we want to 24:24 bring in image since there's an image. 24:27 And then we want to bring in uh link 24:30 because there is a link to the actual to 24:32 the full page, the room page. And then 24:36 let's um let's start with the image. So, 24:39 instead of an image tag, we're going to 24:41 use the image component. And the source 24:43 for that 24:45 um basically this the the room images 24:49 ultimately like I said are going to be 24:51 using the storage buckets in appreite 24:53 but for now what we can do is take the 24:56 images you can see in the theme files 24:58 there's a folder called images and a 25:00 folder called rooms inside of it. So 25:03 let's create a public folder in the 25:05 root. 25:07 So, public and then in that we'll have a 25:10 folder called images and then I'm going 25:13 to move the rooms or copy rooms in the 25:16 theme files images to the public images 25:20 like that. Okay. Then, and even if I 25:23 reload now, it should show just that 25:25 room one because that's in there. But we 25:27 want this to be dynamic. So let's change 25:29 the source to uh back tick slashim 25:34 images slash 25:36 rooms and then we want the dynamic room 25:40 name uh image name which is room image. 25:43 Now with this image component we're it's 25:47 going to make us add a width and a 25:48 height. So I'm going to add a width of 25:50 400 and a height of 100. 25:56 Save that. And now we have our images. 26:00 Okay. So, next thing we're just going to 26:02 kind of go through and add the dynamic 26:04 data. So, we have the name here. Let's 26:06 get rid of that. And let's do room.name. 26:10 We should also change the alt here. So, 26:13 we'll change that to room.name. 26:15 And if we save it, you should see 26:17 different names now for the rooms. 26:20 Okay. Let's move along. Here we have the 26:23 address. So, I'm going to replace that 26:25 with room address. 26:28 Then we have the availability. Let's get 26:31 rid of that 26:33 and add room 26:36 availability. 26:38 Then we get the price. So, I just want 26:40 to replace the number here, the 150. I'm 26:43 going to replace that with 26:46 room dot and it's I believe it's price 26:51 underscore perc_hour. 26:54 Okay, let's save that. Check it out. 26:56 Yep. So, that works. 27:00 Okay. And then for the link here, first 27:02 of all, this a tag is going to be a 27:05 link. And then we want for the href, 27:09 let's make this some back ticks. 27:13 and it's going to be slashrooms slash 27:15 and then the room 27:19 ID or money sign ID. 27:23 Okay. So, if I were to click on one of 27:25 these view rooms, it goes to rooms two 27:27 and it's fine. It's it's 404 is what we 27:30 should be getting at this point. But the 27:32 point is if you go to rooms slash and it 27:35 should have that ID. 27:37 All right. Now, 27:40 there's a heading that I want to add. So 27:42 if we look here, there's this available 27:43 rooms and every page really has a 27:46 heading like that. See the So let's make 27:49 that a dynamic component. So I'm going 27:52 to create a new component 27:55 and we'll call this heading.jsx 28:00 sfc 28:02 heading. And then that's going to take 28:04 in a title. All right. Okay. And then 28:07 I'm going to grab from the index html 28:09 right above that room one is this 28:11 section. I'm going to take that and I'm 28:14 going to put that right in here. And 28:16 then just change this to that dynamic 28:19 title. 28:20 Okay. Now I'm going to bring it into the 28:22 homepage. 28:25 So I'm going to import heading 28:29 and then we're going to put that right 28:31 above the brackets, the curly braces. 28:33 So, let's say heading and we want to 28:36 pass in a title 28:38 of available rooms. 28:44 And there we go. So, that looks a little 28:45 better. 28:47 And I think the last thing that we'll do 28:50 before we get into, you know, getting 28:52 into appite and starting to integrate 28:54 that is do the single page, right? So, 28:56 this page here. Now to create a a page 29:00 or a component for this URL, we have to 29:03 go into our file structure, go into the 29:06 app folder and create a folder called 29:08 rooms. Okay? Because we're going to 29:10 slash rooms. Uh and I we can get rid of 29:13 this fonts folder. That's not necessary 29:15 anymore. 29:17 Uh but in that rooms, we want some we 29:19 want to represent this ID. So we need to 29:22 create a folder with brackets and then 29:24 ID. And then inside that is where we 29:27 create our page.jsx. 29:30 And then let's do sfc. And I'm going to 29:33 call this 29:35 uh room. Let's call it room page. 29:39 And for now just put you could just put 29:42 whatever in here. I'm just going to have 29:44 a fragment and just say room. 29:50 And there we go. So no matter which one 29:52 we go to, we're just going to see room. 29:55 Now we need to get the ID which is in 29:59 the the URL. So we can do that pretty 30:02 easily with Nex.js. And this is a server 30:04 component. Every component you create in 30:06 Nex.js by default is is server side. Um 30:10 if you want it to be a client component 30:12 then you would put use client at the 30:15 top. But what I would suggest is not to 30:19 do that unless you need to. And where do 30:21 you need to do that is if you have event 30:23 handlers or if you're using hooks then 30:26 you do have to have use client and we 30:28 will have client components but this uh 30:31 I want this to stay on the server. Now 30:33 in order to get the ID since we can't 30:35 use hooks on a server component you can 30:38 simply pass in here and dstructure 30:40 params and you can access the ID from 30:43 that params. So here I'm going to say 30:47 const and then I want to dstructure the 30:50 ID from that params. And just to test 30:55 this out, let's say room and then ID. 31:00 So if I come back here, we see room 31:02 three. If I go to this one, room one. So 31:05 now we're able to get that ID. Now we're 31:08 not dealing with a database or anything 31:09 yet. We're just dealing with that that 31:11 simple JSON file. So let's bring that in 31:15 just like we did in the on the homepage. 31:17 So import rooms 31:21 from and then at slashdata 31:24 slashrooms.json, 31:27 right? And then to get the room that I 31:30 want from that, let's create a variable 31:33 called room. And we could say rooms.find 31:36 find pass in a function with room 31:43 and I want to get where the room ID is 31:47 equal to the ID that I just got from the 31:50 URL. Okay. And then we'll handle the 31:53 case if there isn't a room. So if not 31:57 room 31:59 then let's return and we'll just have um 32:02 you know what we'll do is return the 32:04 heading component 32:07 and then we'll pass in title and we'll 32:10 just say room not found. 32:15 Okay we have to bring that component in. 32:18 So we'll say import 32:20 heading 32:22 right and then if there is a room then 32:25 we want to show down here uh let's get 32:28 rid of this 32:30 and inside the brackets 32:33 or fragment whatever you want to call 32:35 this. We're going to also have the 32:37 heading but this is going to be the room 32:39 name. So let's say title and let's set 32:41 that to room.name. 32:45 So let's just try that. 32:48 So, I get room not found, which 32:50 shouldn't, 32:54 which actually shouldn't be uh Oh, you 32:56 know what? 32:58 Yeah, the money sign ID. Let's Let's put 33:01 the money sign right here. There we go. 33:05 Um, I actually think when we when we add 33:08 app, right, we don't have to include 33:10 this, even though it is technically 33:11 money sign ID, but we'll just we'll just 33:14 keep it there for now. And now the rest 33:16 of this page. So under this heading, 33:19 there's quite a bit of HTML and in 33:21 classes. So if we go into the theme 33:23 files and go to room.html, 33:26 we want to get everything under 33:29 this section, right? So this div with 33:32 the bg shadow rounded every that and 33:35 everything in it. So that ends down 33:37 right above where the main tag ends. So, 33:40 right above the main, I'm going to take 33:42 that div 33:44 and everything in it. So, up to right 33:47 here, let's copy that and let's bring it 33:50 into 33:51 uh we want to go into our room page. Go 33:53 right under the heading, paste that in, 33:56 and let's change all the classes. So, 34:00 class, I'm going to select one and then 34:01 command or control shift L. Change it 34:04 all to class name. 34:07 Save that. And now it should look like 34:09 this. 34:11 Okay. Now there's a few things that we 34:14 want to bring in up at the top here. So 34:18 I'm going to bring in image since we 34:20 have an image we need to display. I'm 34:23 also going to bring in link. 34:27 And then there's an I there's a chevron 34:29 icon that I want to bring in as well. So 34:31 it's going to be from React icons. 34:33 Whoops. uh react icon slashfa 34:38 and it's going to be fa 34:42 chevron. Let's see uh chevron left. 34:47 Yeah. Okay. So, we want to bring those 34:49 in. And then let's come down here. 34:53 So, right here we can replace this eye 34:56 tag with the chevron. So, or with the fa 34:59 chevron left. And I'm just going to give 35:01 it a class name of inline and MR1. 35:08 Okay. So that'll give us the little uh 35:10 arrow. Now just to kind of go through 35:13 and change all the static stuff, we'll 35:16 do the image. So the img change that to 35:19 image and the source 35:23 is going to be 35:25 put in some back ticks slashim images 35:28 slashrooms and then we want the room 35:33 image and then for this the alt we'll 35:36 change that to just room.name name. And 35:39 we do have to add a width 35:43 400. And let's do height 35:49 height 100, which doesn't even matter 35:52 what we put here because we're using 35:54 classes, but it it will complain if we 35:56 don't add it. So that should now show 35:58 the image. Then we just want to keep 36:01 moving down. So this here is the 36:03 description. So get rid of that and 36:05 let's add room.escription. 36:11 Okay. Then we got the what do we have? 36:13 Square feet. So this right here we're 36:16 going to replace that with room.sqft. 36:21 Then we have the availability. Get rid 36:23 of that. 36:26 And room dot 36:29 availability. 36:31 Then we get the price per hour. So the 36:33 150 we're going to replace that with 36:36 room dot price 36:40 per hour. See that so far. Good. And 36:44 then the address. 36:46 So that change that to room 36:51 address. 36:53 Cool. And then the booking form that is 36:56 going to be its own component I guess. 36:59 Yeah. Let's we we're not going to make 37:01 it work right now obviously, but let's 37:03 let's take it and and make it its its 37:05 own component. So I want to get it from 37:09 this MT. So basically this div which 37:12 ends 37:14 here. Let's cut that. 37:18 Okay. So this right here, we're going to 37:20 cut that. And then we're going to create 37:22 a new component. 37:25 And we'll call this booking form.js. 37:28 JSX. Let's do an SFC 37:32 booking form. 37:35 And then I'm going to paste in what I 37:36 just copied. 37:39 Save it. And I'm not going to do 37:40 anything else to it. I'm just going to 37:42 bring it into the page now. So we'll 37:45 bring it in right here. So import 37:49 and booking form and then just embed it 37:53 right where I just cut it from. So slash 37:57 booking form. save it and we should see 37:59 the same thing. And then the only other 38:02 thing is that this link, we want to use 38:04 that wherever there's an a tag. So right 38:06 here for the back button, say link and 38:09 change the actual link to go to the 38:11 homepage. So get rid of that. Just 38:14 slash. And I think that's the that 38:16 should be the only 38:19 think. Yeah, that's the only a tag. All 38:21 right. So we have our our page now. I 38:26 think I think I've done everything I 38:28 want to do before we actually implement 38:31 apprite. And the reason I did this is so 38:34 we could just implement the apprite 38:36 database change a couple things to, you 38:40 know, where we get the rooms from. 38:41 Instead of JSON file, it's going to be 38:43 from apprite using the SDK. But all this 38:45 stuff we don't have to touch. We don't 38:47 have to change anything in the in the 38:50 component in the UI. All right. So, now 38:53 let's I'm just going to close this stuff 38:55 up for now. 38:57 And we're going to head over to 38:58 AppRight. And you're going to want to 39:00 create an account if you don't have one 39:02 already. It's free. Just sign up, 39:04 confirm your email, and then go ahead 39:06 and log in. And you should see your 39:08 dashboard, which will look like this. 39:10 And we want to create a project. Okay. 39:13 Now, for everything that I name like the 39:15 project, the database, collections, I'd 39:18 suggest just using what I do just to 39:20 avoid any confusion when we, you know, 39:22 when we add it to our environment 39:24 variables later. So, for the project 39:26 name, let's call it book it and then 39:28 project ID. You can let it randomly 39:30 generate one, but I'm going to call it 39:33 book it all lowercase. Okay, so that's 39:35 our project ID. So, we're going to 39:37 remember that. Click next. Choose a 39:39 region which right now Frankfurt is the 39:41 only one available but you can be 39:43 notified when these other ones are. So 39:45 we'll click create. 39:49 Okay. So our project's been created. Now 39:51 we need to choose the platform we're 39:53 using. We have web flutter uh app. So 39:55 iOS, Android, React Native. We're 39:58 building a web app. So we're going to 39:59 click that. And then for the name I'm 40:02 going to do booket- app. And for the 40:05 host name, I'm going to do localhost 40:08 and click next. Now, this is showing you 40:10 how to install the SDK, although we're 40:12 not using this SDK since we're using 40:14 Nex.js. Um, the one we're going to use 40:17 is called Node- Appite. Um, so you could 40:20 just ignore this right now. So, just 40:21 click next and then next again and then 40:24 go to dashboard. So, now we have our 40:27 dashboard with all of our analytics, 40:28 which obviously we don't have anything 40:30 yet. So, the first thing I want to do is 40:31 create a database. So let's go there and 40:34 then create database and I'm going to 40:36 call this booket and I'm going to also 40:40 give it an ID of booket. 40:45 Now from our database we want to create 40:47 a collection. So create collection and 40:50 we're going to call this rooms and I'm 40:52 going to give it an ID of rooms. 40:57 Okay. So now we can create documents in 41:00 our collection. So if you're not 41:02 familiar with like NoSQL um document 41:04 databases, a collection is similar to 41:07 like a table in a relational database 41:10 and a document is similar to a record. 41:13 Um so let's create a new document. I'm 41:16 sorry. A document is similar to a table. 41:20 Now our collection or our table needs 41:24 columns or attributes. So let's say 41:26 create attribute. And pretty much I 41:29 believe everything we're going to create 41:30 is a string. Um, so we're going to go 41:32 ahead and just click string. And the 41:35 first thing I'm going to add is a user 41:37 ID because every every room that's 41:41 created is going to be created by a 41:43 user. And we need a way to link that 41:45 user to the room. So that user owns that 41:47 room. So let's say user ID. For the 41:50 size, I'm just going to put 100. And 41:52 we're going to say required. And then 41:54 create. 41:56 And then we're going to create the rest 41:57 of our attributes. So another string. 41:59 Let's call this name. Size we'll do 100. 42:02 And required create. 42:06 We also want a description. 42:09 Description. I'm going to make it 42:10 bigger. So we'll do 500. 42:15 And then we want let's see address. 42:19 Size I'll do 250 42:22 required. And then I also have location 42:25 which is like the building or whatever. 42:27 And and we'll put a little placeholder 42:29 or something on the form so that they 42:31 know. Uh size I'm going to do 250 for 42:34 that. And I'm not going to make that 42:36 required actually. 42:39 And then we have what else do we have? 42:42 Um let me just 42:46 check my fields here. Okay. So we want 42:50 availability 42:54 size we'll do 100 42:58 and availability we'll make that 43:00 required 43:03 and then let's do the square feet so 43:05 sqft 43:07 size we'll do like 25 43:10 and I'm not going to make this uh I'm 43:13 not going to make it required 43:15 and then capacity so string capacity 43:18 Capacity size, we'll do 25. And I'm not 43:22 going to make that required. 43:24 And let's do what else? Um price per 43:28 hour. So price per 43:31 hour and size we'll do 25. And I'm going 43:35 to make that required. 43:37 Then we have amenities. 43:40 So amenities and I'll do let's do 250 43:44 for that. Actually, we'll do 500 because 43:47 this is a commaepparated list. And 43:49 amenities, I'm not going to make 43:50 required. And then the last one, I 43:53 believe it's the last one, is the image, 43:55 which is going to be a string. Okay? And 43:58 it's just going to be the image URL. 43:59 Ultimately, it's going to be uploaded to 44:01 storage using appite. And for this, 44:04 we'll do let's just do 250. And I'm not 44:07 going to make that required. All right. 44:08 So, those are all the attributes. Now we 44:11 can actually create some data from 44:13 within this interface. So go if we go to 44:16 documents and then create document. Um 44:18 well actually before we do that let's 44:20 create a user because I want to get that 44:22 user's ID and put it in the document. So 44:25 if we go over here to off and we say 44:28 create user and then just add whatever 44:31 you want here. 44:37 You can skip phone. 44:41 Okay. And user ID that's going to just 44:43 randomly generate. So let's click 44:45 create. All right. So we have our user. 44:47 This is the use this user's ID. So I'm 44:49 going to copy that and then go back to 44:51 databases book it rooms create document. 44:55 And I'm going to paste that user ID in. 44:58 All right. And then for this other data 45:00 I'm just going to grab from the JSON 45:02 file. 45:04 So we'll take this name 45:08 and then we got this description. 45:13 We got address. 45:19 We got location which is like the 45:21 building floor stuff like that. 45:24 Availability is 9 to5 45:29 and square feet. We can just type this 45:31 stuff in. Let's do, I don't know, 22 or 45:34 2,000. And capacity, we'll say 100. 45:39 Price per hour, 150. Amenities, I'm just 45:43 going to grab this. 45:46 Okay. And then the image, let's just for 45:49 now, we'll just say room-1.jpeg. 45:53 All right. So, we'll click next. And 45:55 we're going to create 45:57 that document. And let's create one 45:59 more. So again, I'm going to grab that 46:01 user ID. 46:04 And then for the name, I'll just go to 46:06 this next one here. 46:09 So, executive meeting room. We'll grab 46:11 the description. 46:15 Grab the address. 46:21 Uh that's location. Address. Location is 46:24 going to be this. 46:29 And then availability 46:32 8 to six. 46:35 Then we got square feet, let's say 46:37 a,000. 46:39 Capacity we'll say 75. Price per hour 46:43 120. And then amenities, 46:47 grab this. 46:49 And then image, I'm going to say 46:50 room-2.jpeg. 46:53 JPEG. All right. So, click next and 46:56 create. So, now we have two documents. 46:58 Now, this rooms collection, I want to go 47:01 to the settings. And if we scroll down 47:03 here, we can add permissions. So, the 47:06 permissions that I want are for anybody. 47:09 So, let's click any. I want anyone to be 47:11 able to read. So, if you go to the the 47:14 application, you can see the rooms. So, 47:16 you don't have to log in to see them. 47:18 But let's choose all G I'm sorry all 47:21 users which means authenticated users 47:24 and they can create read update and 47:26 delete. So let's click update. And now 47:29 we have the permissions. 47:32 All right. So we have we have a rooms 47:34 collection. We have our database. We 47:36 have a user. I'm not going to do the 47:38 storage just yet. We'll get to that 47:40 later. Um but let's Yeah, I think we're 47:43 I think we're good here. Uh oh no, we 47:45 need to get the API key. So to do that, 47:48 we go to the overview, scroll down to 47:50 integrations, and then API keys, and we 47:53 want to create a new API key. You can 47:55 give it a name. I'm going to just call 47:56 it booket- API- key. And I'll set it to 48:00 never expire. Click next. And then I 48:03 want to select all the different scopes. 48:06 Create. 48:08 And now we can click to copy this API 48:12 key secret. And what we're going to do 48:14 is create aenv in the root or env.local. 48:20 And let's add apprite_appi_key 48:26 and paste that in. All right. Now, 48:28 there's a couple other things that I 48:30 want to add for environment variables. 48:32 So, I just want to go to the docs real 48:35 quick. 48:36 And if I search for let's say 48:39 next_public, 48:41 it's this environment variables. So we 48:44 just did the key, right? U and that's 48:47 the only thing that's not public. 48:49 And I called it apprite API key. I guess 48:52 we could call it this just to kind of 48:55 just to stick with what the 48:56 documentation says. So we can call it 48:58 that. And then I'm going to grab these 49:01 two. 49:03 So we have our public endpoint. It's 49:07 going to stay this cloud.apprite io 49:09 version one. And then the project ID. 49:12 Remember I said to remember that and 49:14 that's going to be book it. 49:17 So I have a few other things that I want 49:19 to add to this thisv as well. Uh 49:24 including the database ID, the 49:27 collections, whatever collections we 49:29 have and whatever um storage buckets we 49:33 have. So we might as well just add that 49:35 now. So let's copy that down. And this 49:38 is going to be next public apprite 49:43 database and that's also called book it. 49:47 Then we got the rooms collection. So 49:50 that's going to be rooms 49:53 and it's going to be apprite 49:56 rooms 49:59 or I guess we could say collection 50:02 rooms. 50:04 And then we're also going to have a 50:06 bookings collection. So let's say 50:08 bookings 50:10 set that to bookings. All right. And 50:14 then we're going to have our storage 50:15 buckets. So for this it's going to be 50:17 apprite or next public apprite 50:22 storage 50:25 uh bucket 50:27 storage bucket 50:29 and this one is going to be for 50:33 yeah we'll do avatars 50:35 and that's going to be avatars and then 50:38 we're going to have another bucket for 50:39 the just the the room images. So we'll 50:42 call this storage bucket rooms and this 50:46 is going to be rooms. And then the last 50:49 thing I'm going to add is just the URL. 50:52 So it's it's not going to be apprite 50:54 just next public and then URL and set 50:58 that to HTTP 51:01 local uh local host. And this will 51:03 change when you deploy, but for now it's 51:05 going to be localhost 3000. So that 51:07 should that's all we need for our 51:09 environment variable. So, we shouldn't 51:11 have to come back here. So, let's close 51:13 that up. Close that up. And the next 51:17 thing we want to do is add, let's see, 51:21 we want to add the client. Let me just 51:24 see if I can find that the 51:28 Yeah, SDKs. 51:32 Um, 51:37 SDK for node. No, that's not what I 51:40 want. 51:46 Initialize clients. 51:49 Yeah, I think this Yeah, this is it. So, 51:53 basically, we're going to have a config, 51:55 a file in a folder called config. That's 51:57 going to be our appreite client. And 51:59 there's basically two clients that we 52:01 need to initialize. One is the admin 52:03 client and one is the session client. So 52:06 if we look at this up here, admin 52:08 clients should only be used if you need 52:09 to perform admin actions that bypass 52:12 permissions or unauthorated requests 52:14 that bypass rate limits. And that takes 52:17 in your API key. Then you have your 52:20 session client. So this is used to make 52:22 requests to appreite on behalf of the 52:24 enduser. So this is where you you you're 52:27 logged in and you can get your session 52:29 and you would pass your session in. 52:31 Well, in this case, they're getting it 52:33 here, but I'm going to do it a little 52:34 bit different and pass the session in. 52:37 So, to create this again, let's create a 52:40 folder. I'm just going to collapse the 52:41 stuff. I'm going to create a folder 52:43 called config. 52:46 And in that, I'm going to create a file 52:49 called appite.js. 52:53 So basically whenever we do anything 52:55 with apprite in terms of getting from 52:57 the database or authentication it's 52:59 basically going to it's going to come 53:00 from this file. 53:02 So let's start off by importing what we 53:05 need. Uh well actually we need to we 53:08 need to install the SDK. We didn't do 53:10 that yet. So down here let's just run 53:12 npm install. Now it's not going to be 53:15 just apprite. That's that's for the 53:17 client if you're building like a spa. 53:20 Since we're using Next, we're going to 53:21 do install node-app. 53:25 Okay, make sure you do that. 53:28 And then we're going to import up here 53:30 from 53:32 node- appite. And I want to bring in 53:34 here client because that's how we're 53:36 going to initialize just like right 53:39 here, new client. But there's some other 53:41 things we need to bring in. If we want 53:43 to use the database, then we need to 53:44 bring in databases. If we want to use 53:47 account and off, then we're going to use 53:50 bring in account. And if we want to use 53:51 the storage, we're going to bring in 53:53 storage. All right. And then we're going 53:55 to create two different functions that 53:57 initialize again the admin client and 54:00 the session client. So I'm going to do 54:02 the admin client 54:04 first. So let's say const and create. 54:09 We'll say create admin 54:12 client and set that to an async 54:15 function. 54:19 And I'll just copy that down because 54:21 we're also going to have the session 54:23 client. 54:25 So session client and that's going to 54:27 take in session. All right. So with the 54:30 admin client, if we take a look at this 54:33 page again, we just need these three 54:36 things, these three methods. 54:39 Um, actually, 54:42 let's copy this this whole thing. We'll 54:44 do Yeah, we'll copy this whole thing. 54:47 And if you're not on this page, just you 54:48 can just type it out or you can copy 54:50 what I have in my in the in the repo. 54:52 But I'm going to call this client. Set 54:54 that to new client. Now, the endpoint, 54:58 I have these or we have these as 55:00 environment variables. So, instead of 55:02 doing that, let's do process dot oops. 55:07 env dot and then next. Let me just check 55:13 what that is right here. So I'll just 55:15 copy that. 55:18 Okay. So that's going to be our 55:19 endpoint. Then we have the project ID. 55:22 So this right here I'm going to grab 55:25 and we're going to replace this with 55:27 process 55:29 dot oops env. and then dot that 55:35 and then the API key. So this right here 55:43 say process 55:46 env. 55:48 All right. So that creates the client. 55:50 Now there's a few things we're going to 55:52 return here. So let's say return and 55:54 then some curly braces. And we want to 55:57 return an instance of the account. So we 56:00 can say get account and then from that 56:03 we want to return new account and that 56:07 gets passed in the client. Okay. And we 56:10 brought this this account right here 56:12 that we're initializing we brought in up 56:14 here. So now we when we use this file 56:17 somewhere else, we can just import this 56:21 uh this admin client and then we can use 56:24 a new account instance. And we're going 56:26 to do the same thing with the database 56:28 and storage. So, I'm going to copy this 56:30 down twice. And this one is going to be 56:34 databases. 56:36 And then this is going to be databases. 56:38 Takes in the client. And then this will 56:40 be we'll just do this storage. 56:45 Okay. 56:47 And then for the create session client, 56:49 that's going to be very similar. So we 56:51 can actually copy everything here 56:55 and just go right in here and paste it 56:58 in. Except when we initialize this 57:00 client, we don't add the the key. So we 57:03 can get rid of that. All right. And then 57:06 we also don't need to uh return storage 57:09 because we're not doing anything with 57:11 storage with the session client. And 57:14 since we passed in the session, right 57:16 above the return, we're just going to 57:20 check if session. 57:23 And if there's a session on the client, 57:25 there's a method called set session. 57:28 We're going to call that and then pass 57:30 in that session. 57:33 All right. And now all we have to do is 57:35 export. I'm going to export both of 57:38 those functions. So, we want create 57:40 admin client and we want create session 57:43 client 57:44 and save it. And that's it. We shouldn't 57:46 have to come back to this file for 57:48 anything. So, let's close that up. Close 57:51 that up. And now, the first thing I want 57:53 to do with AppRight in our project is 57:56 replace this because this is just from 57:57 the JSON file. We're going to replace it 57:59 with the data from our database. And 58:02 there's a couple ways we could do this. 58:04 We could create API routes that we make 58:06 a request to from our components. and in 58:08 the API routes, use the um the AppRight 58:12 SDK, but I'd rather use uh server 58:15 actions instead of API routes. That's 58:17 kind of the the new new and improved way 58:19 to do things server side. And server 58:22 server actions are just asynchronous 58:24 functions. So, in the app folder, I'm 58:27 going to create a new folder called 58:28 actions. 58:30 And in actions, basically, we're going 58:33 to have a bunch of files here that are 58:34 just single functions and they're 58:37 they're server function or server 58:38 actions. So this one, I'm going to call 58:40 this get all rooms.js. 58:46 And in order to use server actions, we 58:49 need to add use server at the top 58:51 because these only run on the server. 58:53 And then I'm going to import 58:55 the uh create server client a create 58:58 admin client from config app.right. So 59:01 the thing we just created. And then 59:04 couple other things. I'm going to bring 59:05 in revalidate path and that's just going 59:09 to uh update the cache. So if we add a 59:12 new room and we get redirected, we want 59:14 that new room to show on this page 59:16 rather than having to wait and refresh 59:18 it. Uh and then if we want to redirect 59:21 from the server action, we're going to 59:23 bring in redirect from next navigation. 59:27 So that's what we need to bring in. Then 59:28 we're going to create an asynchronous 59:30 function. So let's say async function 59:34 get all rooms. 59:38 And I'm going to use a try catch here. 59:42 And in the try I'm going to dstructure 59:45 databases 59:47 from 59:49 await create admin client because 59:53 remember in that file where is it config 59:57 appite we return a new instance of a 60:00 database called databases. So that's 60:02 what I'm getting here and that databases 60:05 has a bunch of methods on it to to deal 60:07 with the database. So let's go ahead and 60:10 fetch. We'll say fetch rooms. 60:14 And we can do that with const. And then 60:16 we're going to dstructure documents. But 60:18 I want to rename it. I don't want to 60:20 call it documents. I'm going to rename 60:21 it to rooms. And we can get that with 60:24 await databases. And then there's a list 60:28 documents method. And what that's going 60:31 to take in is our database ID and the 60:33 collection. So first let's pass in our 60:36 database ID. So we can say process.env 60:41 dot and then I forget what I called it. 60:45 This right here. So we want that. 60:49 Paste that in. So that's the database 60:50 ID. Then we want the collection which is 60:52 the rooms collection. So I'll just copy 60:55 that down. And let's do appreite 60:59 collection 61:01 rooms. I believe that's Yeah. 61:06 All right. So now before we return the 61:09 rooms I just want to let's say 61:11 revalidate 61:13 uh revalidate the cache for this path 61:17 and we do that simply by calling 61:19 revalidate path and then slash that's 61:22 the path we want to revalidate and then 61:24 we just also want to pass in layout 61:28 and then after that we're just going to 61:29 return the rooms. Okay. Okay. Now, if 61:32 there's an error, we'll do a console log 61:34 and let's say 61:37 uh we'll say failed to get rooms and 61:41 we'll also show the error. And then 61:44 let's redirect 61:46 um we're going to say redirect to 61:50 a page at slash error. 61:55 All right. So, let's save that. Now, I 61:58 want to use this in our homepage, right? 62:01 So, let's go to app page.jsx. 62:04 And we're no longer going to use this. 62:07 We're going to import get all. 62:11 Um, wait a minute. Did I Oh, I don't 62:14 think I exported it. 62:16 Function get No, we didn't export it. 62:18 So, down here, let's say export 62:20 as default, get all rooms. 62:24 And then here we're going to import get 62:26 all rooms from actions. 62:29 And then we should just have to uh 62:34 right above the return we should just 62:35 have to do con rooms. And we want to 62:38 make this function async because we're 62:42 going to use await and then get all 62:44 rooms. 62:46 So let's save it. Cross our fingers. 62:54 There we go. So these remember these are 62:57 the two I added in uh in app right now. 63:01 The reason the images are showing is 63:03 just because of how I named it. I called 63:04 it room one JPEG and room 2 JPEG and 63:07 those are already in the public folder 63:09 uh right here. Okay. So remember we will 63:13 they will be coming from apprite later. 63:16 But yeah so everything is working so 63:18 far. One thing I did notice though is I 63:21 didn't put a key. So since we we have a 63:23 list here, we want to add key and we'll 63:26 set that to room dot and I think we have 63:29 money sign ID. 63:34 Let's just check the console here. 63:36 Um oh I must have missed the class name. 63:40 Uh where is it? Heading 63:45 components heading. Yeah. So I have 63:48 class here. Guess I want to just 63:58 I must have did it somewhere else too. 64:00 Room card. 64:03 See class name. 64:08 Oh, I got CL class name. 64:12 So I'm going to select all those. Change 64:14 it. 64:18 Uh still 64:21 um 64:26 footer. 64:36 Okay. So all the class names are taken 64:38 care of. And let's check this. Those are 64:42 the same things. All right. So we should 64:43 be all set. So now we're getting the 64:45 data from appite. Now, this the single 64:49 room is still coming from the JSON file. 64:51 In fact, we're not seeing it anymore 64:53 because the ID is now different. So, 64:56 let's go to our single file. Um, 64:59 actually, before we do that, we're going 65:00 to create another action to get a single 65:02 room. So, let's create a new file called 65:05 get single room in the actions folder. 65:09 And we can copy what we have in the get 65:11 all rooms because it's going to be 65:13 fairly similar. So, we'll paste that in. 65:15 So, we're bringing in all the same 65:16 stuff. Let's change the function name 65:18 here and here to get single room. 65:24 And then uh let's see. So, same thing. 65:27 We're getting databases. 65:29 And then here, instead of dstructuring 65:31 documents, we're just going to call this 65:33 room. And instead of list documents, 65:36 this is going to be a method called get 65:38 document singular. And it's going to 65:40 take in these two things, the database 65:42 and the rooms collection. But it's also 65:44 going to take in an ID which is going to 65:45 be passed in here. So pass that in 65:48 there. And then we'll pass it as a third 65:50 argument here. And then we want to 65:52 return room not rooms. And then we'll 65:55 say failed to get room. And that should 65:58 do it. We'll save it. Now we can use 66:01 this action in our page which is going 66:03 to be in the rooms brackets ID page.jsx. 66:08 So instead of bringing in the JSON file, 66:10 let's import get single room. 66:14 And remember that takes in the ID and 66:16 we're getting the ID from the URL here. 66:18 So uh let's actually make this 66:22 async. And then we can get rid of this 66:25 stuff here and say const room equals 66:28 await get single room pass in the id. 66:33 And that should give us the room. 66:35 Okay. Okay. So, if we save that, take a 66:37 look. Great. Uh, conference hall 66:40 executive meeting room. There we go. And 66:43 this is not coming from the JSON file 66:45 anymore. In fact, you could delete the 66:47 JSON file if you want. Uh, I'm going to 66:49 keep it there just so you guys have it, 66:50 but it we're not using it anymore. Now, 66:53 we're going to start on authentication. 66:55 So, first thing I want to do is just 66:57 create the login and register pages and 66:59 forms. So, let's go to the app folder. 67:02 And we're going to create a folder 67:04 called login and a file called page.jsx 67:08 in that folder. And same thing with 67:11 register. So we'll create a folder 67:13 called register and file of page.jsx. 67:19 Okay. So in the register, let's do sfc. 67:22 And I'm going to call this register 67:25 page. And just for now, we'll just put 67:28 in a div. and we'll just say register. 67:33 Okay, same thing with the login. Let's 67:35 do sfc. Call this login page and we'll 67:39 just put a div and say login. 67:43 Okay, so now we should be able to click 67:45 on well actually we need to fix the 67:47 links here because they're going to the 67:49 HTML. So in the header component, 67:53 let's change some of these links up. 67:57 So let's see. And we might as well 67:59 change the other ones as well. So for 68:00 instance, booking slashbookings html. 68:03 That should be just slashbookings. 68:05 For add room, um, that's actually going 68:08 to be room slash 68:11 add. 68:12 Then we have the login. So that's going 68:14 to be slash login. 68:19 And then register will be slashregister. 68:24 We got my rooms. This is actually going 68:27 to be slashrooms 68:29 slashmy 68:32 and then login again 68:36 should be slash log actually that's the 68:37 sign out so this doesn't really matter 68:39 but we'll just put that for now then we 68:42 got let's see that's the homepage that's 68:44 fine bookings again so this is the 68:48 mobile menu so just same same stuff uh 68:51 rooms slash add 68:54 and that should be Good. So now if I 68:56 click on register, it takes us to the 68:58 register. Login takes us to the login. 69:01 Now for the forms, 69:03 let's go to our theme files and I'm 69:05 going to go to login HTML. And 69:07 basically, we want to go to where the 69:08 main tag is and we want the div that's 69:11 inside the main tag. Not the main tag 69:14 itself, but the the entire div inside of 69:16 it. So let's grab that. Go to the login 69:20 and I'm just going to replace this. 69:22 We'll put some parenthesis and paste 69:24 that in. Make sure we change the class 69:28 to class name. 69:33 Okay. So, if we save that, there's our 69:34 form. Looks pretty nice. And then this 69:38 right here, no account. This is going to 69:40 go to slash 69:42 register. And we also want to use the 69:44 the link component. So, let's bring that 69:46 in. 69:48 So, import link 69:52 and then 69:55 see we're going to just change this to 69:59 link. 70:02 All right. So, there's our form. And 70:04 then we might as well just add the 70:05 register form as well. So, I'm going to 70:07 just grab from the register HTML in the 70:10 theme. 70:13 Again, we're going to get the div that's 70:14 inside of the main tag. 70:18 which ends right here. So, I'm going to 70:20 copy that and go to register. 70:23 And 70:26 let's paste that in. Change all the 70:28 classes to class name. 70:32 And again, right here, we're going to 70:35 change this to link. And we can autoimp 70:38 import it by clicking that. And this is 70:40 going to be slashlo. 70:45 All right. So if we go to register, 70:50 we have our register page. Uh let's see 70:52 what are we getting for an error here. 70:57 Uh input elements should have 70:58 autocomplete attributes suggested new 71:01 password. 71:07 Um 71:09 did you mean HTML 4? Uh yeah. So for the 71:13 four attributes, so I'm just going to 71:15 select four equals and then command or 71:18 control shift L to select them all and 71:20 change that to HTML 4 equals like that. 71:24 We want to do the same thing on the 71:25 login. So let's see for equals select 71:30 all of them. HTML 4 equals 71:35 and I don't care about this warning 71:37 here. We'll might deal with that later. 71:39 But now what I want to do is create 71:41 middleware so that we can protect 71:44 certain routes. Now before we do the 71:46 actual route protection, I just want to 71:48 show you how middleware works with 71:49 Nex.js. So I'm going to just close this 71:52 stuff up for now. And what we do is 71:55 create in the root, we're going to 71:56 create a new file called middleware.js. 72:01 And middleware is essentially just a 72:03 function that has access to the request 72:06 response cycle and objects. Um, so it 72:09 basically sits between the client and 72:12 the the actual request that's made and 72:15 you have access to those objects and you 72:18 can have it run on every request to any 72:20 page or you can limit it to certain 72:22 routes. And that's what we want to do is 72:25 ultimately we want to have middleware 72:27 that protects certain routes. For 72:30 instance, the bookings, right? Bookings, 72:32 ad room, my rooms, those are all routes 72:35 that we're going to want to protect with 72:37 middleware. So let's start off by just 72:39 creating a simple middleware function 72:41 that just logs the requests and then 72:44 I'll show you how you can limit it to a 72:46 certain page and then we'll get into the 72:47 authentication. So I'm going to import 72:50 next response here. 72:54 Next response and that's from next 72:56 server and then we just want our 72:58 function. So, we're going to export an 73:00 async function called middleware 73:04 and that can take in the request object. 73:09 And let's um I'm going to extract the 73:12 URL path from the request. So, I can do 73:14 that by dstructuring 73:18 path name from the request object and 73:21 there's a property called next URL. 73:24 Okay, so I'm getting the path name and 73:26 then I just want to log that path name. 73:28 So I'll do a console log with back ticks 73:30 and I'll say requested page 73:34 and let's add the path name. Okay, so 73:38 that's all I wanted to do. Now whenever 73:40 you have middleware, you need to call 73:42 the next method to continue onto the 73:46 next middleware. So we want to return 73:48 from this next response dot next. 73:54 Now I'm going to save this and go to my 73:57 console down here. 73:59 And if I reload the homepage, you can 74:02 see right here requested page slash. Now 74:07 it also shows all the assets that are 74:09 being used like the logo, the CSS file, 74:12 um the layout and any images. But you'll 74:16 see if I go to like login then we get 74:19 requests for uh login /lo. If I go to 74:23 register 74:25 requestregister, so it's running on any 74:28 whatever route we hit, it's going to 74:30 run. Now to limit this to specific 74:32 routes, we can just say export const and 74:36 then a config object. 74:39 And in that config object, we want to 74:41 add a matcher. Okay, so matcher is going 74:45 to be an array of routes that we want to 74:47 limit this to. So let's just let's do 74:49 login, right? So what this is saying is 74:52 that this middleware is only going to 74:54 run on the login route. So if we look 74:56 down here, I go to let's say rooms the 74:59 homepage. It didn't run. It's not saying 75:02 requested page slash. If I go to 75:05 register, it's not doing it right. This 75:08 is just from before. But if I go to 75:09 login now, it ran requested page/lo. 75:14 So that's how we can make this only 75:18 pertain to routes that we put in here. 75:22 Now I know we haven't implemented 75:25 authentication yet, but we can we can 75:27 kind of mock it right now and just have 75:29 a variable that says if we're 75:31 authenticated or not. So let's create a 75:34 route that we actually do want to block 75:36 because right now we don't have any 75:38 routes we want to protect. The only ones 75:40 we have working are the register and 75:42 login and the rooms and the single room. 75:45 So, let's do the bookings. We'll quickly 75:47 just create a bookings page. So, in app, 75:50 let's create um we're going to have a 75:53 folder called bookings and a page called 75:58 page.jsx. 76:00 And let's say sfc 76:03 call this bookings page. 76:06 and we're going to return just a 76:08 fragment and I'll just say bookings for 76:12 now. Okay, so we have our bookings page. 76:15 Now I only want this to run, right? This 76:18 middleware to run for bookings. So I'm 76:20 going to add that in here. And now if I 76:23 reload the page, we should see requested 76:25 page bookings. 76:27 Now I want to get rid of this 76:29 functionality. We don't I don't care 76:30 about logging anything. I want to 76:33 protect this if it's whatever any routes 76:36 that are in here. I want to redirect to 76:39 the login page if we're not logged in. 76:41 Now, like I said, we don't have actual 76:44 authentication. So, let's just create a 76:46 variable called is 76:49 authenticated and let's set that to 76:52 false for now. Okay. And then I'm going 76:55 to say if is not 76:58 is authenticated. Okay. So if we're not 77:02 authenticated, then I'm going to want to 77:04 redirect. So we can say return next 77:07 response 77:09 dot 77:11 redirect and I want to redirect to we 77:14 can pass in here new URL 77:18 and then login or slashlo is where I 77:21 want it to go. And then we want to pass 77:24 in a second argument of request. URL 77:30 and then if I go back to bookings and I 77:33 reload it takes me to the login page. 77:36 Okay, I can go to any other page you 77:38 know but I can't go to bookings because 77:41 it's this middleware is working for 77:43 this. Now if I set is authenticated to 77:45 true manually then I can go let me just 77:50 reload this then I can go to bookings. 77:53 Okay. So ultimately when we add our 77:55 authentication we'll have the logic here 77:57 to figure out if we're actually logged 77:59 in. If there's a session and you know if 78:02 we are then it'll show the page. If 78:04 we're not then it'll redirect us. Okay. 78:07 So we're ready now to actually implement 78:10 authentication. So I'm going to do this 78:13 with an action. Okay. So I'm going to go 78:16 into actions and I'm going to add a new 78:18 file here and we're going to call this 78:20 create. Let's say create session.js. 78:27 Okay, because that's what we're doing 78:28 when we log in is we're creating a a new 78:31 session. And basically, we want to 78:35 submit our form to this action, our 78:37 login form. And before we actually have 78:39 it authenticate, I just want to hook the 78:41 form up, show you how we can get the 78:43 form data, and so on. So, it's a server 78:46 action. So, we want to say use server at 78:48 the top. And then we want to have u an 78:52 async function 78:54 called create session. 78:57 And this is going to take in form data. 79:00 And at the bottom let's just export 79:04 default 79:06 create session. 79:08 Okay. Now like I said at the moment I 79:10 just want to get the data. So we'll have 79:12 we'll say email and we can say form 79:16 data.get get and email. 79:21 That should get the email input. And 79:22 then we want the password. So let's do 79:25 here 79:27 password 79:29 and password. And this pertains to the 79:32 name attribute in the in the input. And 79:35 for now, I just want to do a console log 79:37 of the email and password. 79:41 Okay. So let's go into our login form, 79:44 which is going to be app login page.jsx. 79:46 JSX 79:48 and we want to import that action the 79:51 create session. So import 79:54 create session and we can have this 79:58 submit directly to it. Right? So for 80:01 instance um right here we can say action 80:06 and we can set that directly to 80:10 create session and that will submit 80:13 directly to that action and we don't 80:14 have to put a method it uh that will 80:17 just react does that automatically. So 80:20 now if I were to fill this out just put 80:24 something in there and hit login. If I 80:26 look down here, it's logging the email 80:30 and the password. And that's from this 80:33 the that catches the form data. We're 80:35 getting the email and password and then 80:36 logging it. Now, 80:40 I want to use a a newer hook to handle 80:44 to for error handling, right? Because we 80:46 want to be able to show a a toast or a 80:49 notification if the credentials are 80:52 wrong or if they don't fill a field out 80:54 or something like that. So, I'm going to 80:56 be using a hook called use form state. 81:00 Now, this is important. If you're using 81:02 React 19 or higher, then it's going to 81:04 be use action state. They work the same 81:07 way, but after React 19, it's use action 81:10 state. Before 19, it's use form state. 81:13 And right now with Nex.js, if we look at 81:15 React, it's using uh it's using 18. So, 81:19 for me, it's going to be use form state. 81:22 So, we want to bring that hook in. I'm 81:24 going to go ahead and import and we're 81:27 going to bring in use form 81:30 state and that's actually going to come 81:32 in from React DOM. 81:35 Okay. And I'm also going to bring in the 81:38 use effect hook. So yeah, I guess we'll 81:42 just uh I'll put that Yeah, I'll put 81:45 that right here. So import and then use 81:48 effect. And of course that's from React. 81:52 Now, if I save this, I'm going to get an 81:55 error. 81:56 And the reason for that is it says I'm 81:59 importing a component that needs use 82:01 effect. It only works in a client 82:03 component. So, if you want to use any 82:05 hooks, it can't be within a server 82:07 component. And remember, there's server 82:08 components by default. So, what we're 82:10 going to do is just make this use 82:13 client. If I do that, then that should 82:16 clear up the error and I should be able 82:18 to use this hook. Okay. Now, the way 82:21 that we use this is pretty simple. So, 82:23 we're going to come right up here above 82:24 the return and we're going to say const 82:28 and then state 82:31 and then the the action or form action 82:35 and set that equal to use form state. 82:38 And then what we pass in here is going 82:41 to be the initial I'm sorry, the the 82:43 action that we actually want to call 82:45 which is create session and then also 82:48 the initial state. So let's say create 82:52 session and then the initial state will 82:54 just be an empty object. All right. Then 82:58 what we want to do is for this action 83:01 we're going to change that to this to 83:04 the form action. Okay. So we'll just go 83:07 ahead and put that in there. Um, and 83:11 then whenever we use use form state in 83:13 the action itself, it's going to take in 83:16 the previous state as the first argument 83:19 and then the form data will be the 83:21 second argument. 83:23 All right, so let's just see if this 83:24 still works hooked up like this. So I'm 83:27 going to go ahead and just input 83:29 something here and log and it's still 83:31 working. Okay, so again, we just brought 83:35 in use form state. We set the state and 83:37 form action destructure it from use form 83:40 state. The action we want to call which 83:43 is create session and the initial state 83:46 which is just an empty uh empty object. 83:49 And this state once we submit the form 83:52 is either going to be a success true or 83:55 an error. And that's why I'm doing this 83:57 is so if there's an error we can show a 83:59 toast or you can handle it however you 84:01 know however you want to. Now, let's 84:04 purposely throw an error. So, I'm going 84:06 to go into create session, 84:09 and I want to check, let's say, if 84:13 there's not an email or there's not a 84:16 password. 84:20 And let's return 84:22 an object with an error. 84:26 and we'll say please fill out 84:31 all fields. 84:33 Okay, so we're returning this an object 84:36 with an error if this is true. Now, 84:39 right at the moment, we have required 84:42 here and for the password. So, it's not 84:45 going to let us even get to that point. 84:47 So, let's just get rid of the required 84:48 for the email temporarily. I'll put it 84:51 back, but just to test this out. Um, now 84:55 it's not going to do anything right now. 84:56 Even if I do leave the email out, right? 84:59 So, if I submit, nothing happens. Um, 85:02 because we need to check for that. And 85:04 that's where the use effect comes in in 85:06 the in this component. So, we're going 85:10 to go right here. We're going to say use 85:12 effect. 85:14 And let's pass in our function and our 85:18 dependency array. And in the dependency 85:20 array, we want to add the state. Okay? 85:22 because the state is changing. If we're 85:25 sending returning that error, that's 85:27 going to go into this state. So in the 85:29 use effect, we then want to check for 85:32 state. 85:34 And if that's true, then let's 85:36 console.log 85:38 the state dot error, which should be 85:41 that string. This right here, please 85:44 fill out all fields. And remember, this 85:46 is a client component, so it's going to 85:48 be in the client. 85:51 So, I'm going to just reload this and 85:53 let's try to submit without the email. 85:55 And I'm just going to open up the 85:57 console. And there we go. Please fill 86:00 out all fields. Now, of course, we don't 86:03 want to show it down here. We want to 86:04 show it in a toast, but you can see how 86:06 the functionality works. I'm returning 86:09 an error from here. That's getting put 86:11 into the state. And in the use effect, 86:14 I'm checking that's whenever that state 86:16 changes, this runs. And I'm checking for 86:18 that error. and then logging it. 86:22 All right, so hopefully that makes 86:23 sense. Now, what I want to do before we 86:26 move on is let's just set up our React 86:28 Toastify so we have a nice toast message 86:31 instead of a console log. So, we just 86:33 want to install npm install react- 86:39 toastify. 86:41 And then what we can do is go to our 86:44 layoutjs because we need the the 86:46 container for the toast. So let's import 86:50 and we want that to be toast container 86:53 from React Toastify. We also need the 86:55 CSS file which is I think I might have 87:00 to look this up. One second. 87:03 Uh let's see. 87:07 Okay, so it's going to be 87:10 react 87:13 toify slashist 87:17 slash 87:18 and then react toy.css. 87:24 I believe that's it. 87:27 Okay. Now, this toast container we need 87:30 to put somewhere in our layout. Doesn't 87:31 really matter because it's positioned 87:33 absolute, but let's just put it right 87:34 here under the footer. 87:37 And then back in our page, our log uh 87:40 login page, we should be able to import 87:45 toast 87:47 uh import toast from React Toastify. 87:51 And then where we want to call it is 87:53 going to be right here. So instead of uh 87:56 just doing a console log, let's change 87:58 that to toast. 88:03 And now if I try that same thing, 88:06 try to submit without the email, we get 88:09 please fill out all fields. All right. 88:11 Now I'm going to put that required back 88:13 in the email. 88:15 But even if we don't have that, it's 88:17 still going to still going to check it. 88:21 Now, we'll also return a success if 88:24 everything goes all right. Right? If we 88:26 if we're able to log in authenticate, 88:29 we'll have a success true that'll be in 88:33 the state. So let's check for that as 88:35 well. We'll say if state 88:39 success 88:41 then we'll show a success message. So 88:44 toast.uccess 88:47 and we'll just say 88:50 logged in 88:52 successfully. 88:55 And then I want to redirect. So in order 88:58 to redirect from a component, we're 89:00 going to bring in the router. So we're 89:02 going to import 89:04 use router. And that's going to be from 89:06 next navigation, not from next router. 89:10 And then to initialize it, we'll come 89:12 down here and let's say const router. 89:16 Set that to use router. 89:20 And right after we do the success, we're 89:22 going to just call router.push. push and 89:25 I want to redirect to the homepage. 89:28 Okay. So now if we submit as long as the 89:30 email and password is filled out then 89:34 um let's do return 89:38 an object with success and set that to 89:42 true. Okay. Now this doesn't log us in. 89:45 There's no login logic here. But just to 89:48 test that out, make sure that we can 89:50 send back the success. So, we'll just go 89:53 ahead and fill that out. And we get 89:54 logged in successfully. And it redirects 89:56 us. Again, we're not logged in. That was 89:59 just to test things out up to this 90:01 point. Now, we can start to actually 90:03 create the session. So, up at the top 90:06 under use server, we're going to import 90:08 and we're going to use create admin 90:11 client from our app, right? And we want 90:15 to create an account instance after we 90:18 get the form data. So let's go. 90:23 Yeah, we'll go right under this check 90:25 right here and let's say get account 90:29 instance because remember from that 90:32 config file, the apprite config, we're 90:35 returning an instance of the database, 90:37 the account storage. Here we want to get 90:40 the account. So let's say const account 90:45 and set that to await 90:48 create admin client. 90:51 And then I'm going to open up a try 90:53 catch. 90:56 So try catch and I'm going to just this 90:59 return success. I'll get rid of that for 91:01 now. And in the try we want to first 91:04 generate 91:06 oops 91:08 we want to generate a session. So let's 91:12 say const 91:14 session 91:17 and we're going to set that to a wait. 91:19 And then on that account object there's 91:22 a create right here create email 91:24 password session. and we're going to 91:27 pass in our email and password. 91:33 Okay. Then we want to create the cookie 91:38 because we're going to have a cookie 91:40 with the session ID on our in our client 91:42 or in our browser. So for that um we're 91:46 actually going to have to bring in 91:48 cookies up here. So let's import 91:52 cookies and that's going to be from next 91:56 slash headers. 92:01 Okay. And then right under where we 92:04 create the session or right under here. 92:07 So we're going to say cookies. 92:10 So cookies parenthesis and then set. And 92:13 you can call this whatever you want. I'm 92:15 just going to call it apprite dash 92:18 session so that we can identify it 92:20 easily. Then it takes in session 92:24 secret 92:26 and then it takes in an object of 92:29 options and I want this to be an HTTP 92:31 only cookie. So we're going to set that 92:33 to true. We're going to set secure to 92:38 true. We're going to set same site 92:42 to strict. 92:45 And we're going to set expires. 92:49 Um, and you can set this to something 92:51 different if you want, but we can do new 92:54 date. And then on that session object, 92:57 we have an expire property. 93:01 And then the path 93:04 is just going to be slash. Okay, so the 93:06 the root. And then after that is where 93:10 we want to do our success or return the 93:13 object with success 93:16 true. 93:18 Okay. Now if there's an error, so if 93:20 something goes wrong then I'm going to 93:22 do a console log and we'll say 93:26 authentication 93:29 error 93:31 and then we'll just 93:34 pass the error. Um, and I'm going to 93:37 return 93:40 an object with error because remember 93:43 from this function we're either 93:44 returning success true or an error with 93:46 a a string. So that string here is going 93:49 to be invalid. 93:50 We'll say invalid credentials and that 93:53 will show in the toast. 93:56 Okay, because in our component we're 93:57 checking for that error and it will 93:59 show. So let's try it out. I'm going to 94:02 reload and I'm going to do brad atgmail 94:05 and I'm going to put the wrong password 94:07 in. 94:10 So I get invalid credentials. Now I'm 94:12 going to put the right one in 94:17 and I'm logged in. And now I'm actually 94:20 logged in. If I open up my uh dev tools 94:23 here and I go to application, 94:25 we should see cookies right here. 94:28 Appright session. So that's our session 94:31 ID. 94:34 Okay. So now we're able to log in. And 94:37 again, that user is what I created here. 94:40 So if you don't have a user, just create 94:42 one here. Give it a password. And you 94:44 can use that password to log in. 94:47 So now that we're able to log in, I 94:49 think the next thing before we do any um 94:52 any protection or anything like that, I 94:54 want to be able to log out and basically 94:56 kill the session and kill the cookie. So 94:59 let's do this. So, let's create another 95:00 action. And we're going to call this one 95:03 destroy 95:05 cookie.js. 95:08 And we could probably Yeah, why don't we 95:11 just copy the 95:13 Oh, did I say destroy cookie? I meant 95:15 destroy session. 95:18 So, cookie is going to be 95:22 session. 95:24 And then I'm going to copy what we have 95:25 in create session. And then let's change 95:29 this and this to destroy session. 95:37 And we want to bring in let's see this 95:39 time we're going to be using our session 95:41 client because remember we have a 95:43 session now to pass in. So instead of 95:46 admin client 95:48 we want session client and we're still 95:50 bringing in cookies um because we want 95:53 to destroy the cookie. And then 95:57 this is not going to take in previous. 95:58 It's not going to take in anything. So 96:00 we can get rid of that. Uh we can get 96:03 rid of we probably just get rid of 96:05 everything else. I guess we'll leave the 96:07 try catch though. So just get rid of 96:09 that. And then 96:11 everything in the try we'll get rid of. 96:15 And then for the catch, 96:18 get rid of the console log. But we do 96:19 want to return an error if something 96:23 goes wrong. and we'll just say error 96:26 deleting session. 96:29 Okay, so let's add the logic for this. 96:32 So the first thing we want to do is 96:34 retrieve the session 96:37 uh retrieve the session cookie. 96:41 So let's say const session cookie and 96:45 set that to 96:47 cookies and then get 96:51 and remember I called mine appreite dash 96:54 session. So whatever you called it 96:56 that's what you want to put in there. So 96:58 that will that'll get the cookie. Then 97:01 we just want to check for it. So let's 97:04 say if let's say if not session cookie 97:09 then we're going to return an error or 97:12 an object with error and we'll say no 97:18 no no session cookie found. 97:22 All right then we want to go into our 97:23 try and we want to initialize our 97:26 apprite client with the session cookie. 97:29 So here we're going to say const and 97:31 we're using account 97:34 and we're going to await on 97:37 create session client and this time we 97:39 want to pass in the session and we can 97:42 get that with the session cookie and 97:44 then dot value. 97:47 Okay, after we do that we can delete it. 97:50 Let's say delete. 97:55 So we can just we don't need to get 97:57 anything from this. So we'll just call 97:58 await and then account dot and then 98:01 there's a delete session method that we 98:03 can use and we want to pass in a string 98:08 of current. So that will delete 98:12 the current session. 98:14 Okay. Next we want to clear let's say 98:17 clear session cookie. 98:20 So we do that with cookies 98:24 delete 98:25 and of course the name of the cookie 98:27 which is apprite dash session 98:31 and then finally we just want to return 98:35 success 98:37 and set that to true 98:40 and yeah I think that that should do it. 98:44 All right so now how do we want to call 98:46 this? we want to call it by clicking 98:48 this sign out button. So that means 98:51 we're going to go into the header. So 98:54 components header 98:56 and let's see how are we going to do 98:58 this. Um we're going to bring the the 99:02 action in that we just created. So 99:04 import destroy session. We're also going 99:07 to be using the router. So we might as 99:09 well bring that in. I'll just do that up 99:11 at the top here. So import 99:15 use router and that's going to be from 99:17 next navigation 99:20 and then 99:22 in the component here let's initialize 99:25 the router. 99:29 Okay. Um 99:32 now the log out or the sign out button 99:35 right now it's a it's a link I believe. 99:38 Where is it? Yeah, right here. So, it's 99:40 a link and I actually left it as an A 99:42 tag cuz we're going to change this to be 99:44 a button. So, let's get rid of the href 99:47 and change this to a button. 99:51 Okay. And let's just see what that looks 99:53 like. 99:54 Um, 99:57 you're in Oh, we need to make this a 100:00 client component. That's right. 100:03 So, say use client 100:10 All right. So, sign out 100:14 is now a button, which means we can put 100:18 a handler on it. So, let's say on click, 100:22 and we're going to set that to a 100:24 function. We'll call it handle log out. 100:28 Um, what else do I want to do here? Does 100:32 that look right? that icon. 100:36 It kind of looks like 100:39 maybe just cuz it's smaller, but it 100:41 looks like it's up higher than the other 100:42 ones. But I think it's just cuz it's not 100:44 as it doesn't have as much height. But 100:47 yeah, I guess that looks fine. All 100:49 right. So 100:51 that handle logout, let's create that up 100:54 here. 100:58 So we'll say const handle logout 101:03 and that's uh that's going to be an 101:05 async function. So let's make sure we 101:07 add that and 101:11 I want to get the either this the 101:13 success or the error from destroy 101:16 session. So let's say const and then 101:19 we'll dstructure and we'll get success 101:24 error 101:26 from await 101:28 destroy session 101:30 and then we'll check for success. Right? 101:34 So let's say if whoops 101:38 if success then 101:42 um we're going to redirect. So 101:44 router.push 101:48 and just redirect to login. 101:52 Else 101:55 then we're going to show uh a toast 101:57 notification which means we have to 101:59 bring in toast. So let's go up here and 102:02 let's import 102:04 toast from react toify 102:08 and we'll say toast. 102:14 And for the error, actually, you know 102:16 what? We can just yeah, we'll just pass 102:18 in the error that comes from the action. 102:21 So, we should be able to log out now. In 102:23 fact, I'm going to open up my dev tools, 102:26 and you can see the apprite session. And 102:28 I'm just going to reload this. If I 102:30 click sign out, there we go. It kills 102:33 the the cookie or kills the session, 102:35 which also destroys the cookie and 102:37 redirects us to the login. 102:40 All right. And if you go to your your 102:43 your admin interface here, excuse me, 102:46 and you go to your user and then 102:48 sessions 102:50 right now, there's not one there, but 102:52 try logging in and then come back and 102:54 you should see the session. I'm not 102:55 going to show mine just because it shows 102:57 your IP address. Uh even though I'm 102:58 using a VPN, but still. Um but that's 103:01 that's another cool feature as well. 103:04 So uh yeah, let's go ahead and log back 103:07 in. 103:11 And once I log in, that cookie should 103:13 show back up. So there it is. Now I'm 103:16 logged in. So what I want to do now is I 103:20 want to be able to handle these links up 103:23 here because some of these should only 103:24 show if we're logged in or logged out. 103:27 And later on we're going to have a 103:31 context, an O context which will send if 103:33 we're logged in or not down to all the 103:35 components. But right now, let's just 103:37 create the action so that we can use it 103:40 directly in our header to show or not 103:42 show the links that are supposed to. All 103:45 right. So, I'm going to close everything 103:47 except for the header. And then I'm 103:49 going to create a new action called 103:52 check off.js. 103:56 And in this check off, we're going to 103:58 import let's see uh 104:01 what do we want to import here? 104:04 Well, first of all, we need to make this 104:07 use server. 104:10 Okay. And then I want to import our 104:13 create session client. So create 104:17 session client. 104:20 And then we're also going to import 104:23 cookies 104:25 from next headers. Then we'll create our 104:28 function. So async function check off 104:34 and let's export it as default. 104:41 All right. So in check off we want to 104:44 get our session cookie. So let's say con 104:47 session cookie set that to cookies.get 104:52 get 104:54 and the name of it is apprite 104:57 session. 105:00 All right, so we got that. Then we're 105:01 going to just check for it. So if 105:04 there's not a 105:06 session cookie, 105:09 then we're going to return 105:11 an object and we're going to return is 105:14 authenticated 105:16 and set that to false. 105:20 Okay. Okay. Then underneath that the if 105:21 statement, let's do a try catch. 105:25 And in the try, I'm going to get the 105:29 account instance. 105:32 And we're going to get that from await 105:36 create session client. And then we pass 105:38 into this the session cookie dot value. 105:44 Okay. Then we're going to get the user. 105:46 So the way we get that is with you is 105:49 with uh await and then on that account 105:52 object there's a get method. So that 105:55 will get the user and then what I want 105:57 to return 105:59 from this is going to be is 106:02 authenticated which is going to be now 106:04 set to true. 106:07 And then we'll return the user as an 106:10 object with the id. So the ID will be 106:13 user dot and then money sign ID 106:18 and then let's do the name which will be 106:21 user.name 106:23 and email 106:25 user.e. 106:27 So that's what I want to return from 106:29 this. And then if there's an error, 106:31 we're just going to return 106:33 an object with is authenticated and set 106:38 that to false. 106:40 Okay, so pretty simple logic. We're 106:42 getting the cookie, the apprite session 106:44 cookie. If it's not there, then that's 106:47 false. Then we're going to um get the 106:50 account using the session client, get 106:53 the user from the account, and if all 106:56 goes well, return is authenticated true 106:59 and also the user information. 107:02 All right, so let's save that. And then 107:04 I'm going to use this for now. I'm going 107:06 to use it in the header component. So, 107:08 we'll put this uh right here. Let's say 107:12 import check off. 107:17 And let's see. I'm also going to bring 107:20 in 107:22 yeah, I want to bring in use effect and 107:24 use state as well. 107:26 So, use state, use effect 107:33 from React. All right. And then we're 107:36 going to have a piece of state called is 107:38 authenticated. So let's put this right 107:42 here and say const 107:45 is 107:48 authenticated and then set is 107:52 authenticated. 107:54 Set that to use state. And the default 107:56 for this um the default I'm going to 107:59 have as null. 108:01 Okay. Now above the handle logout is 108:04 where I'm going to have the use effect 108:06 because this is where we want to check 108:10 we want to run that check off. So this 108:12 takes in a function and the dependency 108:16 array which is going to be empty and 108:18 we're going to create since I'm using a 108:20 sync await I'm going to create the 108:22 function first. So let's call this fetch 108:25 off status and set that to async. 108:31 um it's going to be an async function 108:35 and we want to just say const result and 108:39 set that to await and this is where we 108:41 use our check off action and then I'm 108:44 simply going to set is authenticated to 108:47 the result dot and then is authenticated 108:51 and that is authenticated is this right 108:54 whether it's going to be true or false 108:55 depending on what happens 108:58 and then of course we need to call this 109:00 within in the use effect. So let's say 109:02 fetch 109:04 O status. 109:06 So now what we can do is use this piece 109:08 of state. This is authenticated to show 109:11 or hide certain things. So let's scroll 109:13 down and I have comments in most of the 109:16 areas where we need to use this. So 109:19 bookings and room and the add rooms 109:21 that's for logged in. So, I'm going to 109:23 go right above it and say is 109:26 authenticated and I'll use the double 109:28 amperand. And then anything in this 109:30 parenthesis is only going to show if it 109:32 if we're authenticated. So, I want both 109:34 of these links. So, I'm just going to 109:37 cut those. And then inside of the 109:39 parenthesis, we'll add an a fragment and 109:42 then paste that in. 109:44 Okay. Then we'll scroll down a little 109:46 bit where it says logged out only. So 109:48 the login and register for those we're 109:51 going to say not is 109:54 authenticated 109:57 and then we'll go ahead and cut the 109:58 login and the register not the building 110:02 or the the my rooms but just these two 110:07 and then in there we'll have our 110:08 fragment and paste that in. And if I 110:10 save now we no longer see login and 110:13 register because we're logged in. 110:15 So let's keep going here. this my the my 110:18 rooms and the sign out. Obviously, we 110:20 only want to see those if we're logged 110:22 in. So again, we're going to say is 110:25 authenticated 110:28 and then grab the link and the button, 110:31 cut those, 110:33 put in a fragment, paste them in. Okay, 110:37 so now these will only be shown if we're 110:39 logged in, which we are. And then let's 110:41 do the mobile menu. So right here, 110:44 bookings and the ad room should only be 110:48 uh should only be shown if we're logged 110:50 in. 110:51 So is 110:53 authenticated 110:56 and we'll grab this and this cut that 111:03 put in fragment and then paste that in. 111:07 Okay. So now if we sign out, 111:13 so notice I had to refresh. 111:15 What we need to do is create a global 111:18 context for authentication. This way we 111:20 can access the authentication state and 111:23 the user from anywhere in the 111:24 application and it will also fix the 111:27 issue of not updating the links right 111:29 away when we log in and log out. Um, if 111:32 I go ahead and log in again, 111:40 you'll see I still just see login and 111:43 register, but if I reload, 111:45 then I see the correct links. So, 111:47 obviously, we don't want that. Um, now 111:50 you could use like Redux or um you could 111:53 use another state library, but React has 111:56 the context API, which for something 111:58 like this, I think is perfect for. You 112:01 don't need anything extra for something 112:03 like this. Now, the check off that we 112:06 just created the this action, we didn't 112:07 create this for for nothing. We're still 112:09 using this, but instead of using it 112:11 right within the header, we're going to 112:13 use it within the context so that we're 112:16 we're pertaining to the entire 112:18 application as a whole globally rather 112:20 than just the header. So, let's start 112:22 off. I'm just going to collapse 112:24 everything here. Um, so we're going to 112:27 start off by creating our context. So, 112:30 I'm going to create a new folder in the 112:31 root called context. 112:34 And in that context folder, we'll have 112:36 off 112:38 context uh.js. 112:41 All right. So, we want to import a few 112:44 things here. And if you've if you've 112:46 used the React context API before, this 112:48 should be very familiar. If not, I'll 112:50 try to explain it best as I can. State 112:53 management has always been kind of a 112:54 pain point at least for me um when it 112:57 comes to explanation but I'll I'll try. 113:01 So let's bring in our create context 113:05 function. So that's going to um we're 113:07 going to bring that in from React as 113:09 well as use context the hook use context 113:13 the use state hook and the use effect 113:17 hook. And all of that is going to come 113:19 from React itself. 113:22 Okay, then I'm going to import the 113:24 action that we just created the check 113:26 off. Okay, so let's Whoops. Let's create 113:31 our context. So const off context and 113:35 set that to the create context function. 113:39 And then we want to we want to have a 113:41 provider. And this provider is what 113:43 provides the the data um or the context 113:46 to our components. So this needs to be 113:49 exported. So we're going to say export 113:51 const and then call this o 113:55 provider. 113:57 Okay. And then this is going to take in 114:00 a prop of children. 114:03 You can think of this as like a 114:05 component that wraps all other 114:06 components. And anything we do in here 114:09 is going to pertain to all components. 114:12 So in here let's create a couple pieces 114:15 of state. So again, we're going to have 114:17 is authenticated, but instead of just 114:20 being in the header, this is now in the 114:21 the main global context. So let's say 114:25 set uh set is 114:28 authenticated 114:30 and use state 114:32 and we're going to set that to false. 114:36 And then I'm going to have a piece of 114:37 state for the current user as well. So 114:40 let's say current user 114:45 and then set current user. 114:52 So use state and we're going to set that 114:53 to null by default. 114:57 All right. Then 114:59 let's return 115:01 we're going to return our cont our 115:04 provider. So in this return let's say O 115:09 we're going to do Ocontext dot provider. 115:14 Now this 115:16 Ocontext provider it's going to wrap 115:19 children. So whatever we pass in it is 115:22 going to be put there. But whatever we 115:26 want to send down in terms of uh either 115:29 data or functions, we can pass in as a 115:34 value. So we're going to set this value 115:36 and it's going to be an object. So it's 115:38 going to be set to double curly braces. 115:41 And I want to send down is authenticated 115:43 so that we can access that from 115:45 absolutely anywhere, any component. And 115:47 then we can tell if we're logged in or 115:49 logged out and we can show or hide 115:51 things depending on that. And then also 115:54 we want to be able to set is 115:56 authenticated from anywhere. So let's 115:58 say set is authenticated. And then I 116:01 also want to send down the current user 116:04 as well as be able to set the current 116:06 user. So all of these should be 116:09 accessible in anything that we wrap this 116:12 provider around. 116:14 Now, 116:16 in the use effect is where I want to 116:18 check uh to use our our action, our 116:23 check off action and see if we're 116:25 authenticated just like we did in the 116:27 header except now we're doing it 116:28 globally. That's really the only 116:30 difference. So, let's say use effect and 116:36 we're going to be using a sync a weight. 116:38 So, I'm going to create the function 116:39 here. So let's say cons check I'll call 116:43 this check authentication. We don't want 116:45 to call it check off because that's the 116:46 name of the the action. So check 116:49 authentication is going to be an async 116:52 function. 116:54 Now what we want to do is dstructure. We 116:57 want to get from check off is 117:00 authenticated and user. So that's what 117:02 we're going to do here is get is 117:04 authenticated and user 117:07 from await 117:11 check off 117:13 and then we want to set the state. This 117:15 this is what actually checks if we're 117:17 authenticated because it it you know 117:19 checks the um the session right. So here 117:23 is where we want to set the actual state 117:25 for our UI or for our application 117:28 globally. So here we're going to say not 117:30 const we're going to say set is 117:33 authenticated and set that to is 117:37 authenticated and then we want to set 117:40 the current user to the user and then of 117:44 course we want to call this function 117:47 check authentication. 117:50 All right. Now, the last thing I want to 117:51 do in this file is create a custom hook 117:54 that we can use in our components to get 117:56 our to get the O status. So, here let's 118:00 say export const 118:04 and we'll call this hook use off 118:07 and set that to function. And this is 118:11 where we use the use context hook. Now, 118:13 you could use the use context hook 118:16 within the component, but it's going to 118:17 be it'd be more work every time you want 118:20 to use it in the component rather than 118:22 just using this custom hook where we do 118:24 it here instead. So, let's say const 118:27 context set that to use context 118:32 and pass in our O context. And then we 118:36 just want to check we'll say if so if 118:40 not context 118:43 then we'll throw say throw new error 118:47 and I'm going to say use off 118:52 must be used within 118:55 an O. Yeah, we'll say within an O 118:58 provider. 119:02 All right. And then we're going to 119:04 return. So after that, if the context is 119:07 there, then we're going to return 119:09 context. 119:11 All right. So that should be it for this 119:13 file. Now we want to be able to use this 119:17 um and remember that the provider has to 119:19 wrap around everything basically. So 119:22 that means we we want to go into our 119:24 layout, right? So let's see, we want to 119:27 go into app and our layout. Now you 119:32 might think well let's bring in the O 119:34 provider to the layout and let's wrap 119:37 everything right like put O provider 119:39 here but the thing is the O provider 119:43 the O provider uses hooks right so that 119:47 means it has to be within a client 119:49 component and I do not want to make the 119:51 layout client like we could add up here 119:54 use client but then the layout and 119:56 everything within it is going to be 119:57 client side which I don't want so the 120:00 solution is to create a separate 120:01 component and create an O wrapper that 120:04 uses that provider and then use the O 120:07 wrapper here, which I know is a little 120:09 confusing, but I mean that's just how it 120:11 how it is dealing with, you know, client 120:13 and server side components. So, let's go 120:16 into the components folder and let's 120:19 create a new file called O wrapper. And 120:22 this is going to be really simple. It's 120:23 not going to be a lot of code. So, 120:25 orapper.jsx JSX 120:28 and let's do sfc call it off wrapper. 120:35 All right. And then what we're going to 120:36 want to do here is make this uh wh make 120:39 this client side. 120:41 So let's say use client. 120:45 And then we want to bring in our o 120:47 provider that we just created. So import 120:51 off provider from our context. And then 120:56 o wrapper it's just going to take in 120:58 children as a prop. 121:01 And then whatever is passed in as 121:03 children is going to be put here and 121:04 just wrapped in the O provider. So O 121:08 provider and then we put in 121:11 children and that's it. So pretty 121:14 simple. Now we can bring this O wrapper 121:17 into our layout and use it and we don't 121:19 have to then make the layout client. 121:22 So where do I want to put this? So just 121:24 go right here and let's import 121:27 uh o wrapper 121:30 and then we can just wrap everything. So 121:33 within the return 121:36 say o wrapper 121:40 and let's end it the very bottom. 121:44 Okay. So now our wrapper is around 121:46 everything. So now we can really 121:48 simplify the header because we no longer 121:50 need to do this stuff locally like the 121:52 you know check this. We don't need to 121:54 use check off in here because it's being 121:57 used in the context. All we need to use 121:59 is this use off custom hook. So let's uh 122:03 let's completely get rid of the use 122:05 effect. Get rid of that. And then uh we 122:10 don't need to bring in use effect. And 122:12 then we don't need to bring in check off 122:14 but we do need to bring in our use off 122:18 our use off hook from the O context. All 122:21 right. Now remember the the O context is 122:25 it we're providing all all of these is 122:28 authenticated set is authenticated 122:31 current user and set current user. We're 122:33 interested in these two values is 122:35 authenticated and set authenticated. And 122:38 it's they're in an object right. So 122:41 here, um, we don't want brackets because 122:45 it's coming from an object. So we're 122:47 going to dstructure and we're going to 122:49 use brackets. I'm sorry, not brackets, 122:52 curly braces. And we're not going to use 122:54 use state anymore. We're going to just 122:55 use use off like that. And that will 122:59 give us those values. And and again, it 123:00 pertains to if we're globally 123:03 um authenticated that global value. 123:07 And yeah, we no longer need use state 123:09 either. So get rid of that. And then the 123:12 last thing I want to do here is when we 123:13 log out, we need to set that 123:15 authenticated value to false. So right 123:19 here we have if success before we 123:22 actually redirect, we want to set is 123:25 authenticated to false. And that will 123:28 make it so that when we log out, the 123:30 menu items will change right away 123:32 because we're setting the global state. 123:34 Right? So if I click sign out, there we 123:36 go. they this changes right away. We 123:38 didn't have to refresh. However, if I 123:40 log back in right now, 123:47 they didn't change, right? I have to 123:49 refresh. 123:51 Refresh. Okay. And the reason for that 123:53 is we didn't set the is authenticated to 123:57 true when we logged in, right? So, we 123:59 basically got to do this same thing 124:01 using the use off hook, but do it in the 124:03 login. So let's go to app login page.jsx 124:08 and let's import the use off hook. And 124:12 then all we have to do is create u just 124:16 like we did in the header. We're going 124:19 to say con and make sure you use uh 124:21 curly braces not brackets. And we want 124:24 is authenticated 124:27 and set is 124:30 authenticated 124:32 from use off. 124:34 Okay. And then all we have to do now is 124:36 go down to where we actually log in. 124:39 Let's go right 124:41 um yeah we'll go right before the 124:43 redirect and let's say set is 124:47 authenticated. And this time we want to 124:49 set to true because we're logging in. 124:52 So now if I sign out and then sign back 124:55 in 125:00 we should see. Yep, there we go. So the 125:03 links now change right away because that 125:06 global state is being changed once I log 125:08 in. It's being sent down to the header 125:10 and we're adjusting it based on um based 125:14 on that is authenticated value. So now 125:18 what I'd like to touch on 125:20 is the middleware because remember the 125:23 bookings remember um we have in the 125:27 middleware.js 125:29 I just have this this hard-coded value. 125:32 If I set this to false then I can't 125:36 access bookings but I want this to go by 125:39 my actual status be able to just import 125:42 the check off action. And then for this 125:45 is authenticated, I'll just wrap it I'll 125:47 just wrap it in um curly braces. 125:52 And instead of just setting it to false, 125:53 we're going to set it to await and then 125:57 check off. 125:59 So let's try that out. If I go to 126:01 bookings, okay, it lets me go to 126:03 bookings, but if I sign out, I can't see 126:07 bookings obviously because I'm signed 126:09 out. But if I try to go to 126:11 slashbookings, 126:14 it redirects me to the login. So now 126:16 it's actually paying attention to if I'm 126:18 logged in or not rather than just a a 126:20 hard-coded value. So the next thing I 126:22 want to do is the registration. So let's 126:24 create uh a new action. And we're going 126:27 to call this create user.js. 126:31 So let's say use server 126:34 and we're going to be using the admin 126:36 client. So import and we want the create 126:40 admin client and then I also want to 126:43 generate an ID for this new user and 126:46 appite includes this ID that we can 126:50 bring in from node app. So we're going 126:53 to use that and then let's create our 126:55 async function and we'll call this 126:57 create user 127:00 and let's export it. 127:03 export default create user 127:07 and then create user is going to take in 127:10 previous state because again we're going 127:12 to use that use form state or use action 127:14 state depending on which version of 127:16 react you're using we're going to use 127:17 that hook and when we use that previous 127:21 state is going to be the first argument 127:22 and then form data is going to be the 127:24 second argument into this action and 127:27 then we want to get our email so we can 127:30 get that with form data 127:33 get and email. 127:36 So we want the email, we want the um 127:39 actually there's a name too. So let's do 127:42 first one will be name 127:45 and then we got password. 127:52 Password and I think that should be it 127:55 as far as fields go. And then let's just 127:57 make sure that all those are filled in. 127:59 So we'll say if not email or not name 128:04 or not password 128:09 then we're going to return 128:12 an object with an error and we're going 128:14 to say please fill in all fields. 128:20 Okay. And then under that if statement I 128:22 also want to check the password length. 128:25 So let's say if the password 128:28 dot 128:30 length is less than eight. 128:33 So if it's less than eight, then we're 128:35 going to return an object with an error. 128:39 And that error is going to say password 128:42 must be 128:45 at least eight 128:48 characters 128:49 or at least eight characters long. All 128:52 right. And then we want to also make 128:55 sure because there's going to be a 128:56 confirm password. 128:58 um which I guess we could put that up 129:00 here as well. 129:03 So we'll call this what did I call it in 129:06 the form? I just want to go to the the 129:08 register form here and let's see for the 129:12 name we have confirm- password. So let's 129:16 call this confirm 129:20 password and we want to form data.get 129:23 get 129:25 confirm dash password 129:28 and then we want to just check to make 129:30 sure that those match. So let's say if 129:34 if the password is not equal to the 129:39 confirm password 129:42 then we're going to return an object 129:45 with an error and we'll say passwords 129:49 passwords do not match 129:53 and I think that's all the validation 129:55 that I want to do. Now we want to get 129:56 the account instance. So let's say get 130:01 account instance. So const and in curly 130:06 braces we want account and set that to 130:10 await. And then we're using our create 130:12 admin client. 130:15 Okay. Once we do that we'll go ahead and 130:18 open up a try catch. 130:22 And in the try I want to try to create 130:25 the user. So let's say create user and 130:29 the way we do that is we can await on 130:32 account and there's a a method called 130:35 create and what that's going to take in 130:37 is an ID and we can use that ID that we 130:41 brought in and there's a method on that 130:42 called unique. Obviously you want it to 130:45 be a unique ID and that's a method. So 130:48 make sure you put in parenthesis. Then 130:50 we want to pass in the email. Then we 130:52 want the password and we also are adding 130:55 a name. 130:57 So that will try to create the user and 130:59 if everything works out then we're going 131:01 to return an object with success 131:05 true. So you can see all these actions 131:07 are formatted. They're returning the 131:09 same thing. It's either an error or 131:11 success. And then in the the catch here, 131:14 let's just do a console log and we'll 131:17 say 131:19 we'll say regist uh register or 131:22 registration 131:24 error 131:26 and we'll pass in the error and then we 131:29 just want to 131:31 um we want to return from that. 131:36 Yeah, we'll just return an object error 131:40 and we'll just say could not 131:43 register user. 131:46 All right, so let's save that. And then 131:48 we want to use this. So we're going to 131:50 hook up the register page, which is this 131:52 right here. This is app register 131:55 page.jsx. 131:57 And there's a few things we need to 131:58 bring in. And we need to make this a 132:00 client component because we're using 132:01 hooks. So use client. And let's import 132:06 first off use effect. 132:10 Oops, don't want it from that 132:16 just react. And then we also want the 132:19 use form state. So 132:22 say use form state. And again, if it's 132:24 React 19 or later, I I'm pretty sure 132:27 it's use action state. I haven't used it 132:30 outside of of Nex.js, but I'm pretty 132:32 sure that's the case. So, react-dom. 132:37 Um, and then we want the router. So, 132:40 let's import use router. That's going to 132:43 be from next navigation. 132:45 And then we're going to use a toast here 132:47 as well. So, let's import toast from 132:50 React Toastify. And of course, we want 132:53 the we want the um action that we just 132:56 created. So, let's import create user 133:01 from actions. Actually, let's use the 133:03 alias. So, it'll be slash 133:06 um or at slashapp slashactions. 133:11 Okay. So, if we go to the register page. 133:14 Good. Now, we're going to do the same 133:17 thing we do with the login with the the 133:19 use form state. So, we're going to do 133:22 right here const 133:24 and we want to open up some brackets. 133:26 And we have state and we have form 133:28 action 133:30 and we set that to use form state and 133:34 that's going to take in the action that 133:35 we want to submit to which is create 133:37 user and then the default state which 133:40 will be or the initial state which will 133:42 be just an empty object. Then we want to 133:45 initialize the router because we're 133:47 going to redirect. So use router and 133:50 then we want to add our use effect 133:57 and we're going to pass in state as a 133:59 dependency. So if the state changes then 134:01 this use effect will run and in the use 134:04 effect I want to check for state. 134:08 And if that's there then let's do toast 134:11 error and pass in state.error which will 134:15 be whatever the message is. And then 134:17 we'll check for success. So same same 134:20 way the login worked. So state success. 134:24 If that's true then we'll do a um a 134:28 toast 134:30 success 134:32 and we'll say you can now log in 134:38 and then we want to just redirect. So 134:40 we'll say router.push push and we want 134:43 to redirect to slashlogin so that they 134:46 can log in. And then the last thing is 134:48 to just add this form action right here. 134:51 We want to add it as the actual action 134:54 of the form. So set that to form action. 135:00 And now we can test it out. So we'll go 135:02 to register and I'm just going to do 135:05 John Doe John atgmail. 135:09 And then remember I I put that it has to 135:11 be eight characters for the password. 135:12 Let's try something that's less than 135:15 that. 135:16 And we get that error. If I do p 135:19 password that doesn't match. Okay. Well, 135:21 they're both they're uh they're both not 135:24 eight characters. So let's do eight 135:25 characters, but let's make them not 135:27 match. And we get passwords do not 135:29 match. So validation seems to be working 135:31 great. So let's 135:34 do something that's acceptable. 135:38 register. 135:40 And now we can log in. And if you go to 135:42 the dashboard here and you look at off, 135:45 now there's the John Doe user. So I 135:48 should be able to log in with John 135:49 atgmail. 135:54 And now I'm logged in as John. Now I 135:56 think we should do since we can log in, 135:58 we can create users, I think we should 136:00 make it so we can add a room. So let's 136:02 add a route for this add room. uh which 136:06 right now we're just getting a 404. 136:08 So I'm gonna go to close this up and 136:13 we'll create the page first which is 136:15 going to be an app and then we'll create 136:17 actually no we want to go into the rooms 136:19 app rooms and then a folder in there 136:21 called add and then in add.jsx 136:26 JSX and we'll do sfc and call this 136:31 add room page. And just for now, I'm 136:36 going to put in uh just a fragment. 136:40 And we'll just say add room just to make 136:42 sure that that shows up if we click on 136:44 this link. And it does. So now we want 136:47 the form which uh we're going to get 136:50 from the theme file. So if we go to ad 136:52 room.html, HTML 136:54 it's going to be 136:57 let's see so in the main tag here that's 137:00 the section so that'll be the heading 137:01 component we already created but we want 137:04 this div with the bgy shadow LG so that 137:07 ends 137:10 uh let's see that ends 137:13 right here right before the main tag 137:15 ends and then we're going to grab that 137:17 with everything in it 137:20 so the div. Okay. And then let's go to 137:24 the page. And actually before I paste 137:27 that in, let's also import the heading 137:31 component because we're going to put 137:33 that in here as well. 137:36 So we'll say heading 137:40 and pass in a title. 137:43 And for the title, let's say add a room. 137:48 All right. So, if I save that, we should 137:50 see the title or the heading. Then, I'm 137:52 going to paste in the form that I just 137:54 copied. And we're going to change all 137:56 the classes to class name. 138:02 And what is this? Oh, we must have 138:04 comments in here. Yeah. So, the image 138:07 upload comment. 138:10 And I think that's it. 138:13 Yeah, that should be it. So, now here we 138:16 have our form. 138:18 Uh so the basically we're do we're going 138:21 to do the same kind of um flow that we 138:25 did with the login and the register 138:27 forms. We have our use form state and we 138:31 have an action that we submit to and we 138:33 check for errors and so on. So that's 138:35 kind of that's the workflow that you 138:37 want to keep in mind when you're 138:38 building Nex.js applications. It doesn't 138:41 even have regardless of if you're using 138:43 apprite or or something else. That's 138:45 just kind of the flow that that I would 138:47 suggest at least is to use server 138:49 actions rather than the the old way of 138:51 using API routes and making fetch 138:53 requests. 138:55 So trying to think of how I want to kind 138:59 of order this. Um I guess let's create 139:02 the action first and then we'll just 139:05 hook it up to the form. So let's jump 139:07 into actions and we'll create a new 139:11 file. Let's call it create room.js. 139:14 JS 139:17 and trying to think if I can just copy 139:20 something. No, I guess we'll guess we'll 139:22 just uh type this out. So, let's say use 139:27 use server 139:29 and we want to import and we're using 139:32 the admin client. So, let's say create 139:35 admin client. Bring that in. And then we 139:38 want to bring in check off as well. So, 139:41 we're going to import check off. 139:45 And let's we're going to need an ID. So 139:48 we're going to bring in that ID from 139:51 node app, right? And we want to 139:54 revalidate the path. So we're going to 139:56 bring in revalidate 139:59 path from next cache. 140:03 Then we'll create our function. So async 140:06 function create room. 140:11 And since we're using the that hook, the 140:14 use form state, we're going to use we're 140:16 going to pass in previous 140:18 state and then the form data. 140:23 And let's export this as default. Create 140:26 form or create room, sorry. 140:31 All right. Now, we want to first off, 140:34 we're using um databases. So we want to 140:38 get the database or databases instance 140:43 and we can do that with const and then 140:47 databases 140:49 and we want to await 140:52 on the create admin client. 140:56 All right. And then we also want to uh 140:59 to get the user 141:02 from the session which we can do with 141:04 that check off. Now I'm going to put 141:06 this in a try catch. 141:08 Okay. So in the try let's say const and 141:11 then we can get the user 141:14 and set that to a weight check off. 141:20 All right. And then let's say if there's 141:23 no user 141:26 then we're going to return an object 141:28 with an error and we'll say you must be 141:34 logged in to create a room. 141:39 All right. And we're going to we're not 141:41 even going to let the user go to the 141:43 page to create a room um if they're not 141:46 logged in. So, we don't we don't 141:48 necessarily need this, but just just in 141:50 case. Um, actually, let's go to the 141:52 middleware.js real quick because I don't 141:54 think 141:56 I don't think I added that as a 141:59 protected route. Yeah, I didn't. So, we 142:01 just have bookings right now. Um, so we 142:04 want to just add to this 142:07 slashrooms slash add. 142:11 And we might as well add the other ones 142:13 like slashrooms slashmy. 142:17 And I think there's some others, but 142:19 those are the only ones I can remember 142:21 at the moment. So we'll just put those 142:22 there. All right. So checking the user. 142:25 If there's no user, we send an error. 142:29 And then if there is a user, we keep 142:31 going and we create the room. And we do 142:34 that. Let's say con new room. We do that 142:38 with await databases dot and then create 142:42 document. 142:46 Now this create document is going to 142:48 take in a few things. It's going to take 142:49 in the the database 142:51 uh or the database ID which we have in 142:53 our environment variables. So process 142:56 envite 143:05 database. Let me just double check that. 143:10 ENV local. 143:12 Oh, and another thing, I'm not using 143:14 avatars. I don't know why I put this 143:16 here. Initially, I was going to, but I 143:18 decided not to. So, we don't need that 143:21 for the storage. Um, let's see. So, 143:23 yeah, the next public apprite database. 143:27 And then we also need the rooms 143:28 collection. So, I'm going to grab this. 143:31 And as a second argument, we're going to 143:34 pass in process 143:37 env. 143:40 That's not right. That's the storage 143:42 bucket. We need the collection, which is 143:45 this. 143:51 All right. And then the third thing we 143:52 pass in is the ID. So we're going to do 143:55 ID do unique 143:58 like that. All right. Then we want the 144:01 data for the new room. So we're going to 144:04 pass that in as an object. 144:07 So we'll say user 144:10 ID and that's going to be the user 144:12 that's logged in which we now have 144:14 access to user and then we have access 144:16 to the ID. Okay. So we got the user here 144:20 through the session using our check off 144:22 state. And now we're just adding that as 144:24 the user ID. 144:27 Then we want the name. So the name, all 144:30 this stuff is going to come in through 144:31 the form. So let's say form data.get 144:35 and name. 144:38 Then we have the description. I'm just 144:40 going to copy that down. And then we'll 144:43 select this and this. 144:46 Say description. Oops, I spelled that 144:49 wrong. 144:51 There we go. Description. 144:54 Okay, we'll copy that down. Next one is 144:56 going to be the square feet. So, let's 144:58 grab this 145:00 and this and sqft. 145:05 Okay, copy that down. Next thing is 145:07 capacity. 145:12 Then we got location. 145:19 Okay, we got address 145:27 and we got what else? Availability 145:36 and price per hour. 145:41 So price underscore perc_hour 145:45 and we'll copy that down. Then we want 145:47 amenities. 145:55 And yeah, I think that's it. I'm not 145:57 going to deal with the image just yet. 145:59 So, we're going to leave that. It should 146:01 It's not required. So, it's okay if we 146:03 don't have it. Now, after that, after we 146:05 create the room, so that ends right 146:08 here. We want to revalidate path and we 146:12 want to revalidate slash and layout. 146:17 and then just return 146:20 success 146:23 true. 146:25 Okay. And then if there's something 146:27 wrong, we're going to console log the 146:31 error. And then I believe we can just 146:34 return 146:36 error. Now this error I think it has a 146:39 response 146:42 a response object. 146:46 Um let's try this. Let's say const error 146:49 message 146:50 set that to error.response 146:56 message or 146:58 then we'll just say an error or an 147:02 unexpected 147:05 error has occurred. 147:07 and then we'll return error and set that 147:10 to the error message. 147:14 Okay. 147:17 Yeah. So, let's try to hook this up. 147:20 So, 147:23 I'm going to go to my my form now, my ad 147:26 room page form. And a couple things that 147:29 we're going to want to bring into this. 147:32 Um, so up at the top here, 147:35 let's import. We need use effect 147:44 and then we need the use form state 147:49 or use action state depending on your 147:51 version of react and that's from react 147:54 DOM. 147:56 Then we get the router. 148:00 So use router from next navigation and 148:03 we already have the heading. Let's 148:05 import the toast. 148:07 So toast from react toify 148:11 and we want the the uh action that we 148:14 just created. So that is going to be 148:16 create room. 148:18 Now we're going to do the same thing. So 148:20 this should look pretty familiar. Let's 148:22 say const and we want to pass in state 148:25 and form action 148:28 and set that equal to use form state. 148:33 This is going to get passed in the 148:34 action we want to use which is create 148:36 room and then the default or the initial 148:39 state which is just brackets or curly 148:42 braces. Then let's initialize the 148:45 router. 148:50 Okay. Okay, then after that we're going 148:52 to have the use effect 148:55 and pass in our function 148:58 dependency array and dependency array is 149:00 going to have the state. So if that 149:02 changes then this runs and we want to 149:05 check for an error. So state error then 149:10 let's toast error 149:14 and pass in the state error. 149:18 Okay, if there's a success. So, state 149:20 dot success. 149:24 So, if that's true, then we want to show 149:26 a success. 149:29 And we'll just say room 149:33 created successfully. 149:37 And then we'll redirect. So, 149:39 router.push. 149:42 And we're going to redirect to slash 149:48 And what do we got here? You're 149:50 importing a component that needs use 149:51 effect. Oh, so we need to this needs to 149:53 be a client. 150:03 Use form state is not defined. What do I 150:06 do? Use form action. No, use form state 150:10 or use action state. Okay, so let's try 150:14 this out. The image isn't going to get 150:15 uploaded, but that's fine. I just want 150:17 to give this a shot. So, room name, I'm 150:20 just going to say room one. And I'm 150:21 going to leave a bunch of dummy data and 150:24 click save. 150:27 And oh, I forgot to add the action right 150:30 here. So, this needs to be form action, 150:36 which comes from right here. And 150:38 ultimately, it calls the create room. 150:40 So, let's try this again. Make sure I 150:42 save that. 150:44 Okay, fill this out. Room one. 150:49 Save. 150:51 Room created successfully. Cool. So, 150:54 looks good so far. We got and the again 150:56 the image we'll we'll deal with that 150:58 soon. View room. 151:00 There we go. We have our booking form 151:02 which we have yet to set up or hook up. 151:05 But yeah, looks good so far. So I think 151:10 the next thing that we should deal with 151:12 is images. Now to deal with images we we 151:16 need to create a storage bucket. So that 151:19 means we need to go back to our apprite 151:20 interface and go to storage. And here 151:24 I'm going to say create bucket and I'm 151:26 going to call this bucket rooms because 151:29 that's what it is. It's going to be 151:30 images of rooms. And I want the bucket 151:32 ID to be rooms. Don't don't randomly 151:35 generate it. just call it rooms. 151:39 So create. 151:41 Okay. So now we have the bucket. Now as 151:44 far as uh permissions, I'm going to go 151:46 to settings and I'm going to click for 151:48 add permissions. And then for any I'm 151:51 going to say read. It's okay for them to 151:53 see it to read. But then for all users, 151:56 I want them to be able to create, read, 151:59 update, and delete. 152:03 All right. So um and again you should 152:07 have since I called if you called it 152:09 rooms like I did then you should have in 152:11 your v local you should have your 152:15 storage right here appreite storage 152:18 bucket rooms and rooms if it's a 152:21 different ID put that ID here 152:25 so 152:26 we want to we want to integrate this 152:29 into our create room action because 152:31 that's where we want this to happen. So, 152:34 let's go 152:37 uh let's go just below where we check if 152:39 the user we check for the user right 152:42 here and right here. Um and then I'm 152:44 going to 152:46 initialize 152:48 uh a variable called image ID 152:53 and then I'm going to get the image from 152:55 the form data because we have a we have 152:56 an image field or sorry a file field in 152:59 our form. So let's say const image set 153:03 that to form data.get 153:07 and then image and the file name should 153:11 be image. Right? If we go to our form 153:14 and we go down to the bottom where the 153:15 file where the upload is right here you 153:19 should have uh yeah you should have this 153:22 name image. If your name is is something 153:25 else then that's what you would put in 153:26 here. Now I want to check for a few 153:30 things. So let's say if and let me just 153:33 put a comment here. This is where we're 153:37 are uploading 153:39 image 153:42 and we're going to check for let's say 153:44 if image 153:46 and image do size 153:50 is greater than zero 153:53 and 153:55 image. 153:57 name is not equal to 154:01 undefined. So we're just making sure 154:04 there's an image there. And then we want 154:06 to do a try catch. 154:10 And this is where we want to do the 154:11 actual upload. 154:14 So to upload we can say const 154:19 uh const response 154:21 and then we want to um bring in where we 154:25 have where we brought in databases. I'm 154:27 also going to bring in a storage 154:29 instance and I can do that because of 154:32 that config file that we have right just 154:34 to kind of remind you 154:37 what where this is coming from. We're 154:39 using the create wh the create admin 154:41 client and we have this storage uh a new 154:45 storage instance that we're returning 154:47 from here. So we have access to that and 154:49 that's what we're bringing in and we 154:52 want to use that down here. So the 154:54 response is going to equal and we want 154:57 to await on storage and there's a method 155:00 called create file. 155:04 Okay. And then in that create file, we 155:06 want to put the storage bucket, which is 155:08 rooms. And then we want to put the ID 155:13 that we want to use. So I'm going to do 155:15 ID unique. 155:17 And then the the image, which is this 155:21 right here, getting it from the form. 155:23 All right. So that should do the actual 155:25 upload. And then I want to set the image 155:29 ID. 155:31 So image ID, we're going to set that to 155:33 response dot and then money sign ID 155:38 because that image ID is what's going to 155:40 go in the database. This will do the 155:42 upload, but we want to save this in the 155:45 database. Um, now I'm just going to do 155:48 in the catch, we'll do a console log and 155:51 we'll say 155:54 error uploading. 155:57 Uh, upload I can't type. uploading image 156:02 and then we'll just pass in error and 156:06 then we want to return 156:10 error and same thing we'll just say 156:14 error uploading image. 156:18 All right. Um, now this if statement 156:22 here, I'm going to put an else 156:25 and then I'm just going to do a console 156:27 log 156:28 and say uh we'll just say no image file 156:35 provided or file is 156:38 invalid. 156:42 Okay. Now all we have to do is come down 156:44 here. This is where we we add our 156:47 document and we want to add image and 156:51 set that to the image ID. 156:58 All right. And I think that that should 156:59 work. So, let's save it and let's try to 157:02 create a new room. So, I'm going to go 157:05 to add room 157:08 and I'm going to just fill it out with 157:10 some dummy data. But for the title, I'm 157:12 going to say room with image. So I know 157:15 that this is the one where I tried to 157:18 upload an image. And I'll just grab one 157:20 of these and click save. 157:26 Okay. So room created successfully. Now 157:28 it's not going to show here. It's not 157:30 we're not prepared to show images from 157:32 apprite yet, but we can check to see if 157:34 it actually got uploaded by going to 157:36 storage and then going to our rooms 157:39 bucket. And there it is. 157:41 So, our upload is working fine. Now, we 157:44 just want to be able to show the image. 157:47 We want the image on the card and we 157:49 want it here on the room page. And if 157:52 there isn't an image, then I just want 157:54 to show like a a default. In fact, I 157:57 have it in the theme files. If we look 157:58 in theme files, images, there's this no 158:01 image, which just says no photo. So, 158:04 what I'm going to have you guys do is 158:06 just copy that no image from the theme 158:08 files and put that in your public 158:11 images. So, right here, public and then 158:14 right in images. I'm going to paste that 158:16 in. Not in the rooms folder, but just 158:18 right in images. 158:21 All right. So, now let's start off with 158:23 the card. Okay. That's where I want to 158:25 start. I want to create uh show the 158:27 image first. So, let's go to components 158:30 and then room card. 158:34 And let's see. We're gonna 158:37 we're going to go, 158:41 how do I want to do this? Yeah, we'll 158:43 just go right here above the return. 158:46 And we want to get two things. We want 158:48 the bucket ID and we want the project 158:51 ID. And I'm going to put those in 158:52 variables because we need to put these 158:54 inside of a URL. And to use the process 158:58 do whatever inside the URL, it's just 159:00 going to be too cluttery. So let's 159:02 create a we'll say bucket ID and we're 159:06 going to set that to process 159:09 env. 159:14 So this is going to be the rooms bucket. 159:18 Yeah, right here. 159:21 So I'm going to grab that. 159:25 Paste that there. That should be right 159:27 after the dot. And then we want the 159:32 what else did we want? The project ID. 159:34 So let's say const project ID. And then 159:39 that's going to be set to process.env 159:45 dot and then 159:47 project ID. That's this right here, 159:50 which is just book it. 159:53 Okay. So we'll paste that in. 159:56 So now that we have that, let's 159:59 construct the image URL. So const image, 160:02 and this is all in the documentation on 160:04 how to do this. So image URL, we're 160:07 going to use back ticks and it's going 160:09 to be https 160:12 and then cloud.apprite.io 160:18 io slashv1 160:21 slashs storage 160:24 slashbuckets 160:26 slash and then this is where we want the 160:28 bucket ID. So bucket 160:32 id and then slashfiles 160:36 slash and then we want the the room 160:38 image. Okay, we have the room here 160:40 coming in from the props. So let's put 160:42 in here room dot image and then after 160:47 that we want to do slash view 160:50 and then we want to put on a query param 160:52 here of project and set that equal to 160:57 the project um project ID. So you see 161:01 how how long this URL is. Now I'm going 161:04 to have a variable called image source 161:08 image src. And this is where we want to 161:11 check to see if there's a room image. If 161:13 there is, we want to use this URL. If 161:15 there's not, we want to use the the 161:17 dummy image, the no photo image. Um, so 161:22 let's let's do if there's a room image, 161:27 then use image URL. Put that into this 161:30 variable. Else, then we want to put 161:33 slashim images slash and then no 161:37 dashimage.jpeg. JPEG. If you called it 161:40 something else, then put that there. And 161:42 then we should just be able to come down 161:44 here and in the source, just get rid of 161:47 this and just do image source like that. 161:52 Okay, let's save it. 161:54 Invalid source prop. Okay, so what's 161:57 happening here is with Nex.js, if you 161:59 want to use images from another party, 162:02 from a third party, um you have to add 162:05 that to your config, your Nex.js js 162:08 config. So let's open that up. In the 162:10 root we have nex next.jsconfig.mjs. 162:15 And this is where we want to just add 162:17 into this object right here. We want to 162:20 add images. And we're going to set that 162:22 to an object that has a remote patterns 162:26 array. 162:28 And in remote patterns, we're going to 162:30 have an object with protocol. Set that 162:34 to https. 162:37 And then we want host name. Set that to 162:41 cloud.apprite.io. 162:46 And then we have path name. 162:49 And path name. We're just going to put 162:51 in double asterisk. 162:53 All right. So we'll save that. Let's 162:55 reload. 162:57 Cross our fingers. 163:03 Okay, awesome. Now, these two aren't 163:05 showing because remember those are just 163:07 pointing to static images or or they 163:10 include static images, but we're not 163:12 pointing to them anymore. We're pointing 163:13 to the either the no photo. If there 163:16 isn't one, and if there is one, then 163:17 it's pointing to the image URL. It we're 163:20 still not going to see it on this page, 163:22 the view room, because we haven't done 163:24 that yet. But this is exactly what we 163:27 should be seeing. 163:29 All right. And if you want, you can 163:32 delete these two. I think that's what 163:34 I'm going to do is go in my database 163:35 here 163:37 and delete the two. 163:39 Um, yeah. So, 163:42 this and this. Going to delete. 163:49 And that might just take a second. There 163:51 we go. Okay. So, now it only includes 163:53 the im the the rooms that I've added 163:55 through the application. 163:58 So now let's take care of the this part 164:00 this image. So the room card is all set. 164:03 We can close that up. Now we want to go 164:06 into the the single post single listing 164:10 which is going to be app rooms brackets 164:14 id page.jsx. 164:16 And basically we want to do the same 164:17 thing. So I can actually open that card 164:19 back up because I can just copy from it. 164:22 Yeah. So basically, we want to get the 164:24 bucket ID, the project ID, the URL. So 164:28 this whole this whole thing right here, 164:29 I'm just going to grab. 164:32 And then we're going to put this 164:35 um 164:37 yeah, we'll just put this right here. 164:40 Okay. And then for the image which is 164:43 being displayed here, we're going to 164:44 replace this with image source. 164:51 Um, image source is not defined. 164:56 Oh, it's I put img. It's image. 165:01 There we go. Simple as that. 165:05 So, as you can see, uploading images to 165:08 these buckets is it's pretty simple and 165:10 to get them is pretty simple. Now, I 165:12 want to work on the myrooms page, which 165:14 we haven't created yet. So, we're just 165:16 going to see a 404. So, why don't we 165:18 create the page first? So, we'll go to 165:22 uh let's go to rooms. And then in rooms, 165:24 I'm going to create a folder called my. 165:27 And in my we want to have a file called 165:30 page.jsx. 165:32 And let's say sfc. We'll call it my 165:36 rooms page. 165:39 And for now, let's just put brack uh 165:41 frag fragment. And we'll say my rooms. 165:46 So now this should show. If we go to 165:51 my rooms 165:53 now, I think that the first thing we'll 165:56 do here is create the action to get the 165:59 user's rooms. So, let's create a new 166:02 action. 166:03 And 166:05 I'm going to call it get my rooms.js. 166:10 And when I say my rooms, I'm sure most 166:12 of you you get this already, but I mean 166:14 the rooms that you create, not the rooms 166:16 you book. We're not We're not at 166:17 bookings yet. 166:19 So, in the action, I think we could copy 166:23 probably copy get all rooms. 166:26 Yeah, why don't we do that? Why don't we 166:27 just grab get all rooms, paste that in. 166:30 Let's replace this and this with get 166:34 my rooms. And we're going to let's see, 166:38 we're going to replace a couple things 166:42 here in the import. So first off, we're 166:44 using the create session client 166:48 and we want to get the session. So we're 166:50 also going to import cookies 166:53 from next headers. And then in order to 166:58 get the documents 167:00 of the the current user, we need to add 167:03 a query. And there's this query object 167:06 that we can use from node appite. So I 167:09 want to bring that in as well. 167:12 Uh, and then I don't think we need to 167:14 revalidate path here. So, we can get rid 167:16 of that. So, in the get my rooms, let's 167:21 start off by getting the um the session 167:25 cookie. So, we'll say con session 167:29 cookie and set that to cookies 167:31 parentheses.get 167:33 parenthesis 167:35 and pass in our app 167:37 dash session. 167:40 And then we'll check we'll say if not 167:44 session cookie 167:46 then let's redirect not return redirect 167:52 and we'll redirect to just slash login. 167:56 All right. Now down in the in the try 167:58 catch I want to get databases but I also 168:01 want to get account 168:03 from the create session client. So get 168:06 rid of this. It's going to be session 168:08 and then we're going to pass into this 168:11 the session cookie. 168:17 Then we want we want to fetch uh well 168:20 first of all before we fetch the rooms 168:22 we need to get users get the user's id 168:28 and we can get that by let's say user 168:32 set that to await and then account and 168:36 there's a method called get 168:39 and then we'll say con user 168:43 id and set that to the user user dot and 168:46 then money sign ID. So that'll give us 168:48 the user ID. Then we want to fetch let's 168:52 say users rooms 168:55 and I'm going to documents. 168:59 Let's see. Do we want to keep this as 169:00 rooms? I had this as listings for some 169:02 reason. Are we using rooms somewhere 169:05 else? No. So I guess we we can keep it 169:07 at rooms. And then we're still going to 169:09 use list documents. We're still going to 169:11 pass in both the database ID and the 169:14 collection, but we also want to add a 169:16 query as a a third param here. So that's 169:21 going to go in brackets. And then we can 169:22 say query uppercase Q dot 169:26 equal 169:28 and we want to pass in user 169:32 ID. That's the attribute or the field we 169:34 want to match. We want to match it to 169:36 the user ID right here that we got from 169:40 the session. 169:43 Okay. And then I don't believe we need 169:45 to revalidate here. So we just want to 169:47 return rooms. And then if there's an 169:50 issue, we'll say failed to get 169:53 user rooms. 169:56 And yeah, that should do should do it. 169:58 So let's save it. And now we want to use 170:00 this action. So let's go back into the 170:03 page here. from my rooms page and we're 170:06 going to 170:08 import 170:11 get my rooms. So the action we just 170:13 created and I also want to import the 170:16 heading 170:17 component and I want to import 170:20 uh well actually we haven't created it 170:22 yet but I don't want to use the room 170:24 card because I want it formatted a 170:26 little bit differently. It's going to 170:28 have like um it's going to have a delete 170:31 button and stuff. So we'll we'll create 170:33 that in a second. But let's just see if 170:35 we can fetch the rooms and at least show 170:37 like the name. So if we come down 170:42 into the function and we say const rooms 170:46 and and we need to make this 170:47 asynchronous. 170:49 So async because here we're calling 170:52 await 170:53 and then get my rooms. 170:56 Okay. And then down here, let's get rid 170:59 of this. 171:02 And we're going to actually let's add 171:03 the heading here. So heading. 171:06 And then for the title of the heading, 171:09 we're going to say my rooms. 171:14 And then underneath that, let's check 171:16 rooms.length 171:20 and say if that's greater than zero, 171:23 then some parenthesis. else some 171:26 parenthesis in the first parenthesis 171:28 that's if there are rooms so let's say 171:30 rooms.m map 171:33 and we'll say for each room then I want 171:37 to just show an H3 for now with the room 171:42 name 171:44 okay else so in these parenthesis then 171:46 we're just going to have a paragraph and 171:48 say we'll say you have no 171:53 room listings things. All right, let's 171:55 save that. 171:58 And there we go. So, room one and room 172:00 one with image, those are both mine. 172:01 Now, if I sign out, 172:04 that was the John Doe user. So, now I'm 172:06 going to sign in with Brad at Gmail. 172:12 So, if I go to No, I'm sorry. If I go to 172:15 my rooms here, you have no room listings 172:18 because this user hasn't added one. So, 172:20 let's go ahead and add one. 172:23 I'll say Brad's room. 172:27 Choose a file. Just choose one of these. 172:30 Save. 172:34 There we go. Brad's room. And now if I 172:37 go to my rooms, 172:40 I have my Brad's room listed. Cool. So 172:44 now let's create the card that I want to 172:46 show for each of my rooms. So, I'm going 172:49 to create a new component, 172:52 and I'm going to call this one my room 172:56 uh my roomcard.jsx. 173:00 And we'll create this. Let's call it my 173:03 room card. 173:06 Okay. Oops. And this is going to get 173:09 passed in a prop of the room just like 173:12 the regular room card. 173:15 And as far as what we want to return, 173:17 let's do 173:19 uh let's see. Do I want to 173:22 Yeah, I think we have this in the 173:24 themes. 173:25 So yeah, my rooms.html. 173:31 So if we come down 173:35 my room one. Yeah. So, right under the 173:38 section, 173:40 there's this div and this wraps. 173:45 Yeah, this wraps the card. And there's 173:46 only one here. So, right above the main 173:48 tag, that div. I'm going to grab that. 173:51 It starts right after the section. So, 173:53 I'm going to copy that chunk. 173:55 And then we're going to paste that right 173:57 in here. 174:00 And let's just kind of go through and 174:03 well, first let's bring in what we need. 174:05 There's some a tags. So, we want to 174:07 import link. 174:10 And then I'm also going to have an icon, 174:12 a couple icons. So, we're going to bring 174:14 in FA 174:16 trash for the delete. And then FA I for 174:21 the for the view 174:23 view button. And that's going to be from 174:26 React icons slashfa. 174:31 All right. Now, let's make this dynamic. 174:33 So the room name, we're going to change 174:36 this right here to room.name. 174:41 Then this a tag, let's change it to a 174:44 link. And where we want this to go is 174:47 going to be what is this for? This is 174:50 for the view, just a regular view page. 174:52 So, I'm going to put in 174:55 some back ticks and it's going to go to 174:57 slash rooms slash and then the room ID 175:02 or money sign ID. And we also got to 175:05 make sure we change all these classes. 175:07 So, command or control shift L class 175:11 name. 175:13 And let me just make sure I'm not doing 175:15 that anywhere else. 175:23 And it looks like I might be. Yeah. Oh, 175:26 DOM property 4. So on the the add form 175:29 or the create form or rooms add 175:31 page.jsx. 175:33 Anything with a four attribute we need 175:36 to replace. So I'm just going to 175:37 highlight for equals, select them all, 175:40 and then HTML for equals. 175:44 Should get rid of that. 175:48 And let's see what one of them has. 175:51 HTML. 175:54 Looks like I might have did it for some 175:55 of them, but not all of them. So, I'm 175:57 just going to search for HTML 4. 176:00 And then 176:04 what is it? HTML. Oh, 176:08 HTML HTML 4. 176:20 Okay. All right. So, back to the my 176:22 rooms. So, basically the the room card 176:25 here, the only thing I have left is the 176:27 icons. So, this right here, this I tag, 176:30 we're going to replace with F AI. 176:34 And I think did I want to add class? 176:37 Yeah, we'll just add the inline class. 176:40 So we'll say inline as well as MR1. 176:46 Okay. And then this icon here, the trash 176:49 icon. F A trash 176:55 and inline. 176:59 All right. So, and the but we'll hook 177:02 the delete button up very soon. Probably 177:04 do that next. But let's show this room 177:06 card for all of our rooms. So, back in 177:09 the page, which is rooms my page.jsx, 177:14 let's bring in 177:16 the my room card. 177:19 And then right here, instead of an h3, 177:22 we're going to say my room card. And 177:26 we're going to pass in a key which is 177:28 going to be the room dot 177:33 id and then we have the room itself 177:37 which we're going to pass in room. 177:40 Okay. So there we go. So we get the name 177:43 of the room and then we have the view 177:45 button which will take me to the p the 177:47 room and we have the delete button. Now 177:50 I'm not going to add an update button if 177:52 you want to. That's a a kind of a a 177:54 challenge that you could do on your own. 177:56 Um, you should know how to do that. It's 177:57 it's really nothing new. It's all the 177:59 same stuff we've been doing. But the 178:01 delete button, let's hook that up real 178:03 quick. So, that's going to have its own 178:05 action as well. Uh, let's see. I'm just 178:07 going to close this up. All this stuff. 178:10 And let's create a new action 178:13 called delete room.js. 178:18 And for this, 178:20 see, how do I want to do this? Um, 178:25 I guess let's just copy. 178:33 No, not that. Let's Let's just copy get 178:39 my room. Get my rooms. We'll just copy 178:41 that whole thing. Okay, we'll paste that 178:44 in. Let's change get get my rooms here 178:46 and here to delete rooms. 178:50 And then we want to bring in pretty much 178:52 all this same stuff. But in addition, we 178:55 do want revalidate path here. 178:59 Let's bring that in. 179:02 And we want query, we want everything 179:04 else. And the delete room or it's going 179:07 to be delete room singular. 179:11 And it's going to take in the room ID. 179:13 So let's say room ID. 179:16 Okay, we're we're going to do this same 179:17 thing. Get the session redirect if 179:20 there's no session. And then in the try, 179:22 we're getting the account and databases 179:25 from create session client passing in 179:27 the session value. And then we're going 179:29 to get the user's ID. So this is all 179:32 good. Um and then here we're getting 179:35 documents 179:37 with list documents. 179:39 Um we do want to get just the user's 179:42 rooms. Then we want to find the room to 179:44 delete. So right under that let's say 179:47 find room to delete. So we'll say const 179:52 room 179:54 to delete and set that to listings or 179:57 not listings. I called it rooms. So 179:59 rooms.find. 180:03 And we want to pass in here an ar arrow 180:05 function 180:08 um with the room. 180:10 And we want to find where the room money 180:15 sign ID is equal to the room ID that's 180:18 passed into the action. And that's the 180:21 room we want to delete. Then we want to 180:23 delete the room. 180:26 And we do that with well first off let's 180:29 just say if room to delete. 180:33 So if room to delete then we're going to 180:36 await 180:38 and we want to call databases.delete 180:42 document 180:45 and delete document is going to take in 180:47 both the database ID and the collection. 180:50 So I'm going to copy those from here and 180:53 then just paste those in. 180:55 Okay. And then it's going to take in a 180:58 third argument of room to delete and we 181:02 want the ID. So dot money sign id. 181:06 All right. Now after that 181:10 we want to revalidate 181:14 my rooms and all rooms. 181:19 So let's say revalidate path 181:22 and we want to revalidate slashrooms 181:26 slashmy 181:29 and pass in layout. And we want to do 181:31 the same thing for the homepage. 181:35 So we want to add we want to just do 181:36 that as well. Then we can return. Let's 181:40 go under 181:42 uh let's see. 181:47 Yeah, let's go right under that 181:49 revalidate and let's return 181:52 an object with success 181:56 and set that to true. 181:59 Okay. Okay, we can get rid of this 182:00 return rooms. 182:04 And then um I do want to have an else 182:08 for this if room to delete. So that ends 182:10 right where does that end? Yeah, that 182:13 ends right here. 182:16 So yeah, let's put else. 182:20 Then let's return an error 182:25 and we'll say room not found. Okay. And 182:29 then in the the catch here, we'll just 182:31 say failed to 182:34 delete room. 182:39 And yeah, I think that 182:42 that should do it. Actually, I don't 182:45 want to redirect here. I want to return 182:47 an error. 182:55 All right. So that's our delete action. 182:59 Now we want to hook that up. So that's 183:01 going to be let's see 183:04 we have the button in our my room compon 183:07 my room card right. So here it is. Now 183:11 we could add everything into into this 183:14 component but 183:16 I don't want to make this a client 183:18 component. So what I'm going to do is 183:20 create a separate component from the 183:21 delete button itself. So in components, 183:24 let's create a new file. And we'll call 183:26 this delete room button.jsx. 183:33 And let's say sfc 183:35 delete room 183:37 button. And this is going to be a client 183:40 component. So use client. 183:44 And we're going to import a couple 183:46 things here. So now we want the trash 183:48 icon here because the button is no 183:50 longer going to be in the my room card. 183:52 It's going to be in here. So, fa trash 183:55 from React icons. And then also, this is 184:00 where the action is going to be run. So, 184:02 we want to bring in the delete room 184:04 action. And then also the toast. So, 184:07 import 184:09 toast from React Toastify. 184:12 Now, in the my room card, we no longer 184:14 will need the trash icon. So, we can get 184:16 rid of that. And we just want to grab 184:19 the button here and cut it. Go to our 184:23 delete room. 184:25 And let's paste that button in. 184:30 All right. Now, this is going to have an 184:33 on click handler. So, let's say on 184:36 click, and we want to set that to a 184:39 function. We'll call it handle 184:42 um yeah, handle delete. 184:47 And then let's create that function 184:49 right here. So const handle 184:53 delete and let's set that to 184:57 um an async function 185:03 and that handle delete. I do want to 185:06 have a conf a confirmation here. So, 185:08 let's say const confirmed. 185:13 And let's set that to window dot 185:18 confirm. And we'll just say, are you 185:21 sure you want to 185:26 delete this room? 185:30 Okay. So, we'll do that. And then we'll 185:32 say if 185:34 confirmed 185:37 then we're going to go ahead and delete 185:38 it. Now the delete room button is 185:40 actually going to get passed in the room 185:42 ID. 185:43 So it'll get passed in as a prop and 185:46 then if confirmed then let's do a try 185:49 catch and in the try we're going to say 185:52 const response and then await 185:57 um the delete room action that we just 185:59 created. And that's going to get passed 186:01 in the room ID. 186:03 After that, we're gonna just do a toast. 186:09 And we'll say room 186:12 deleted successfully. 186:17 And then in the error, we'll just do a 186:19 console log and we'll say 186:23 failed to delete room 186:26 with the error. 186:29 And we'll do a toast error 186:33 and we'll just say failed to delete 186:36 room. 186:39 All right. Now we want to bring this 186:41 delete room button into the my room 186:43 card. So here let's import 186:48 delete room button. And then what we 186:50 want to do is is paste this in 186:54 down here where we had the button 186:55 initially. 186:57 So, delete room button and we want to 187:00 pass into this the room ID which we can 187:03 set to room dot and then money sign ID. 187:09 All right. So, we should still see the 187:10 same thing, right? 187:13 Okay. So, we see the same thing. And now 187:15 I'm going to click on delete. I'm going 187:17 to click okay. 187:20 Room deleted successfully. And it goes 187:22 away. 187:24 Awesome. 187:27 So, yeah, we can do 187:29 we're getting there. I mean, the only 187:31 thing left is is the bookings. We can 187:33 create rooms. We can view them. We have 187:36 our our image uploading 187:39 full authentication. So, yeah, I think 187:41 now it's time to get into bookings. All 187:44 right, guys. So, I went ahead and I just 187:46 added some some new rooms here with some 187:49 data, all the data that was in the JSON 187:51 file. And three of them are from a 187:53 different account. And then one of them 187:54 is from the account that I'm currently 187:57 on, which you can see here. All right. 187:59 And if you want to do the same, you can 188:01 just, you know, pause the video, add 188:02 some more rooms. And now we want to deal 188:05 with bookings, which right now we have 188:08 this page that just says bookings. And 188:10 before we write any more code, we need 188:12 to create uh a bookings collection. So 188:14 if we go to our databases, go to our 188:16 booket database, we want to create a new 188:19 collection and call it bookings. And we 188:22 also want the ID to be bookings. 188:26 So we'll click create. And then there's 188:28 four attributes we want. We want the 188:31 user ID, the room ID, the checkin, and 188:35 check out. And check in and check out 188:37 will be date time. So let's click 188:40 attributes and we're going to add a 188:42 string of user 188:44 ID 188:46 and for the size, I'll say 100 and 188:48 required. 188:50 Next, we're going to have the room ID, 188:52 but this we want to be a relationship 188:54 because it's going to have a 188:56 relationship with the the rooms 188:58 collection, right? So, let's click on 189:00 that. And it's going to be a one-way 189:02 relationship. 189:04 And the collection is rooms, the only 189:07 other collection we have. The attribute 189:09 key is going to be room ID. And then the 189:13 type of relation is going to be many to 189:15 one. And if you look right here, it 189:17 explains what that is. bookings can 189:19 contain one room ID and room ID can 189:22 belong to many bookings. So that's the 189:25 type of relationship we want. And then 189:27 I'm going to choose cascade for deleting 189:30 because if we delete a room then we want 189:34 the the bookings to cascade and we want 189:36 to delete those as well. So let's click 189:39 create. 189:41 All right. Then we want to add another 189:44 date. We want a date time field and 189:46 that's going to be check_in 189:50 and I'm going to make that required. 189:54 Okay. Then we want the another date time 189:58 and that's going to be check 190:00 underscoreout 190:02 and that will be required. So those are 190:04 the only attributes we need. Now, as far 190:06 as permissions, we're going to go into 190:08 settings here and add permissions for uh 190:12 for all users cuz right now there's no 190:14 permissions, right? There's nobody can 190:17 do anything. So, we want to say for 190:19 logged in users, we want them to be able 190:22 to create, read, update, and delete. 190:25 We're not going to add read for any 190:27 because obviously 190:29 you're not going to be able to read 190:31 bookings if you aren't logged in because 190:34 you're not going to have any. So that's 190:36 what we want for the permissions. Now we 190:39 should be all set to jump in and get 190:41 started with the code. And I think the 190:43 first thing I want to do is create the 190:44 action. That's kind of been the workflow 190:46 here is we create the action, then we 190:48 create the the form or the page and 190:51 integrate the action into it. So let's 190:54 go to app actions. And I'm going to 190:57 create a new file here called 190:58 bookroom.js. 191:02 And let's copy let's copy get my rooms. 191:07 So I'm just going to grab all that. 191:09 Paste that in. And we'll just kind of go 191:11 through and change it. So as far as what 191:14 we're bringing in, we're using the 191:15 session client. We're using uh let's 191:18 see. I don't think we need cookies. 191:22 Do we need cookies? Yeah, I'm sorry. We 191:24 do. uh we don't need query but we but 191:28 what we do want is the ID because we 191:31 want to generate a unique ID and 191:34 redirect we'll keep that and then the 191:36 only other thing is the check off we're 191:39 going to bring that in so check off 191:41 action and then let's change the name 191:44 here and here to book room now back up 191:50 here we want to pass in the previous 191:53 state because we're going to be using 191:54 the use form state or use action state 191:57 hook and we want previous state and then 192:00 the second will be the form data and 192:03 then this is going to stay because we 192:05 want to get the session cookie check for 192:07 it and then we're going to get we don't 192:10 need the account we just dealing with 192:12 the database so just databases 192:15 and we pass in our session cookie value 192:18 and then I want to get the user from 192:21 check off 192:23 so let's a const 192:26 uh and we want to get user. So we're 192:30 going to deconstruct user and then await 192:33 check off 192:35 and then we'll check for that user. So 192:37 let's say if or we'll check if not user 192:42 and if there's not a user then we're 192:44 going to return an error 192:48 and we'll just say you 192:52 you must be logged in 192:55 to book a room. 192:59 All right. Now this stuff here I'm just 193:02 going to delete for now. And then in the 193:07 error, we'll just say failed to 193:10 book room. 193:12 And then I don't want to redirect here. 193:14 I actually want to return an error. 193:20 And for this I'll just say 193:22 something went wrong 193:26 booking the room. All right. Now, back 193:29 to right here in the try. We need to 193:32 basically 193:35 uh if we look at our form right our 193:36 booking form. So if you go to any room 193:39 there's a check-in date, checkout date, 193:41 check-in time, checkout time. But in the 193:44 database we have two fields which is a 193:46 check-in and check out and that's a date 193:48 time. So it's it's they're you know 193:51 they're both. So we need to basically uh 193:55 combine these into date times. So, first 193:58 off, we're going to extract 194:02 oops, 194:04 let's say we're going to extract the 194:07 date and time 194:10 um from the form data. 194:14 So, let's do that. Uh and then after 194:16 that we're going to combine 194:20 we'll say combine date and time to it's 194:25 going to be it's the ISO 8601 194:29 format 194:31 which has the the date and the time. So 194:34 right here let's do first checkin date 194:39 and we're going to get that from the 194:41 form data.get get 194:45 and the name of that is check_in 194:48 date. And just to remind you, if you 194:51 look at the form, so components 194:55 booking form, we're looking at this 194:58 right here, this field, check-in date. 195:01 So that gets that. Then I'm going to 195:03 copy this down three more times because 195:06 now we want the check-in time. So let's 195:09 change the name of the variable to 195:11 check-in time. And then whoops. 195:15 And then the 195:17 name of the field is check-in time. 195:21 Then the next thing is going to be the 195:23 checkout date. So check out date and 195:27 then this will be checkout 195:30 date. And then we want checkout time. So 195:34 check out time 195:38 and check 195:40 out time. All right. Now we want to 195:44 combine those into just having a date 195:47 time field. So let's create the first 195:49 one which will be called checkin 195:52 date time. 195:55 And we're going to do that by getting 195:58 uh actually we'll use let's use back 196:00 ticks here. 196:03 and we're going to get the check in date 196:06 that we just created. And then we put a 196:09 uppercase t between that and the time. 196:12 So after that, we're going to say check 196:15 in time. 196:20 Um, 196:22 what am I missing here? 196:26 Check-in date. Check. Oh, check-in time. 196:30 Okay. And now we want to do the same for 196:32 checkout. So let's just change this. 196:36 So check out date time and then this 196:39 will be check out date and this will be 196:42 checkout time. 196:46 All right. So that gives us the the data 196:48 we want to add to the database. Now 196:51 let's put that all into an object. So 196:53 we'll call this booking data. Set that 196:56 to an object. And we want the check 197:00 underscore in which is going to be the 197:02 check in date time. Then we want the 197:06 check out date time. That's going to be 197:10 put in for check underscoreout. 197:13 Then the user ID 197:16 we get that from the user we just got 197:19 above and then we can just do ID. 197:23 Now for the room ID 197:26 that we don't have that available to us 197:28 here. So what we're going to do is in 197:30 the booking form we're going to add a 197:32 hidden field for this for the um for the 197:36 ID. So let's go within the form. 197:42 So right here and I mean we don't have 197:44 to we could wait to do this but we might 197:46 as well just add it now. So let's say 197:48 input 197:50 type is going to be hidden 197:54 and let's say for the name that's going 197:57 to be room 197:59 id and then for the value 198:03 that's going to be brackets 198:07 and room dot oops room dot money sign 198:13 ID. 198:15 So when we submit this form, we'll have 198:17 that uh 198:19 uh let's see. Okay, so we're getting 198:21 this error because it doesn't know what 198:22 room is. Room is going to be passed in 198:24 here as a prop. So why don't we go to 198:27 where that's p where this is embedded, 198:30 which is in app rooms 198:33 ID page.jsx 198:36 booking form. We'll just add room 198:39 and we want to pass in room. All right, 198:42 so that'll fix that. 198:44 So yeah, we're just going to have this 198:46 hidden field and that gets submitted as 198:48 room ID. So now we can just simply add 198:51 here 198:52 form 198:54 data.get 198:56 and the name which is room ID. 199:01 All right. 199:03 So now we have our our booking data. Now 199:05 we need to create the actual booking. 199:10 And we can do that. Let's see. We're 199:13 going to create a variable called new 199:15 booking. 199:17 And we're going to set that to await. 199:19 And this is where we use 199:20 databases.create 199:24 document. 199:26 And this is going to take in four 199:28 things. The first two are going to be 199:29 the database ID and the collection which 199:32 we can get from one of these other. 199:34 Yeah. So right here. Uh actually we do 199:36 need the I to create an ID as well. So I 199:39 can just grab those three from create 199:41 room. 199:43 paste those in. And then the fourth 199:45 thing is going to be the booking data 199:47 that we just created. 199:50 All right. And then after that, we just 199:52 want to revalidate. 199:54 Say revalidate cache. 199:57 So revalidate 200:00 path 200:01 and 200:04 um we want to revalidate for 200:06 slashbookings because that's where 200:07 they'll be listed. and then just pass in 200:10 layout. 200:12 Okay. And then we just want to simply 200:14 return success. 200:16 So success, set that to true. 200:21 And yeah, I think that that should do 200:23 it. So let's save it. Now we want to use 200:26 this action in our booking form. So 200:29 let's go to booking form and let's add 200:32 up here. We're going to use client since 200:34 we're using hooks and stuff. 200:38 and let's import what we need. So, we're 200:41 going to need use a I can probably just 200:44 copy this from um one of the other forms 200:48 like the login. 200:51 Yeah. So, I'll just grab 200:55 all these 200:59 and let's see. We're not going to need 201:01 link. 201:03 We do need use router. We need use 201:05 effect. 201:06 We need use form state. We need toast 201:10 um create session. We don't want that. 201:15 What we do want though is our book room 201:17 action. So let's import book room. 201:22 All right. So the room's getting passed 201:23 in here. Um 201:27 let's add let's let's use the use form 201:30 state or use action state depending on 201:33 what version of React you're using. 201:36 So we're going to say state and form 201:39 action 201:42 and that's from use form state. 201:47 Okay. And then that's going to take in 201:49 the action we want to use which is book 201:51 room and the initial state which is just 201:54 an empty object. 201:56 Then let's initialize the router. So use 201:59 router. 202:02 Then we're going to have our use effect. 202:04 So this is the same thing we've done on 202:05 all of our other forms. In the use 202:08 effect, we'll have our function, our 202:10 dependencies, which is going to be 202:12 state. 202:14 So when the state changes, this runs and 202:16 we're going to check for an error. So 202:19 state error. If there is one, then we'll 202:22 toast. 202:24 And the message will be in state. 202:29 Okay. Okay. And then if state success, 202:33 if that's true, then let's show a toast. 202:40 And we'll say room 202:44 has been booked. 202:47 And then I want to redirect. So router 202:50 dot oops router dotpush. 202:54 And we're going to push to 202:56 slashbookings. 203:00 Okay. And then finally down here in the 203:02 form we have to make sure we add the 203:04 action 203:06 of form action. 203:12 All right. So let's try it out. 203:17 Uh yeah, we'll just book this room. So, 203:19 I'm going to say for tomorrow 203:23 and let's do 203:26 let's say 11. 203:30 Yeah, we'll do 11:00 a.m. to 203:35 let's say 1 203:37 p.m. 203:39 Book room. 203:42 Okay, so we're getting something went 203:43 wrong. Let's check the 203:45 document invalid structure. Missing 203:49 required attribute name. 203:58 Oh, I know what what I did wrong. 204:01 So, in the the action, I use the rooms 204:05 collection. That's not what we want. We 204:08 want the bookings collection. 204:12 Okay. Okay. And you should have this in 204:13 your environment variables. But let's 204:15 try that again. 204:18 And there we go. Room has been booked. 204:20 It's not going to show here because we 204:22 haven't added that functionality. But if 204:24 we go to bookings now, we should see 204:29 documents. There it is. 204:33 Awesome. So, it has the user ID. It has 204:36 the the room ID which is a relationship 204:39 to the other the other collection check 204:43 in and check out. 204:46 Awesome. Of course, we want to show the 204:48 bookings. So, we're going to have a new 204:50 action for that. We can close up the 204:51 book room and booking. We can close up 204:54 all this. And then let's go to 204:58 uh actions and we're going to create a 205:00 new file here. And what do we want to 205:02 call this? Let's call it get. We'll say 205:05 get my bookings.js 205:11 and then I'm going to copy 205:13 get my rooms 205:17 and paste that in. Okay. And then we're 205:20 just going to change this and this to 205:22 get my bookings. 205:27 And we want to bring in create session 205:29 client cookies. 205:32 Um what else? Yeah, redirect query. We 205:36 are going to use that as well. But we 205:38 also want the check off. So let's import 205:41 check off. 205:44 And then let's see. We're going to do 205:46 this. Get our session check for it. And 205:49 then we just want databases here. So we 205:52 don't need a count. 205:54 And let's see. 205:57 Uh for the user, we're going to use 206:00 check off. So let's get rid of that and 206:03 say const user 206:05 set uh actually we need to dstructure it 206:10 user and that's going to be from await 206:13 check off 206:15 and then we're going to check for the 206:17 user 206:22 and return error 206:28 say you must be 206:32 you must be logged into view bookings. 206:37 All right. So after the if statement 206:39 then we want to fetch the user's 206:41 bookings and it's going to be similar to 206:44 this. Let's rename this to bookings 206:46 though. 206:48 And then we're going to use list 206:50 documents. Pass this in but make sure we 206:53 change this to our bookings collection. 206:57 And then this this is actually going to 206:59 stay the same. Well, we do want to 207:01 change this because we don't have this 207:03 variable. We have our user and we have 207:06 the ID property on that. So, we just say 207:09 user do ID, 207:11 but we do want to match the user ID 207:13 field. And then let's return bookings 207:17 not rooms. 207:21 And then here we'll just say failed to 207:23 get user bookings. And let's return 207:28 an object with an error and just say 207:32 failed to get 207:35 bookings. 207:37 All right, so that should do it for the 207:39 action. Now, we want to use this and 207:42 where we're going to use this is in the 207:43 bookings page. So, if we go to app 207:46 bookings page.jsx, 207:49 I'm going to bring in a couple things 207:51 here. So one is the heading component 207:55 and then we want 207:57 uh what else? So we're going to have a a 208:01 separate card for this as well, but 208:03 we'll we'll create that after. So let's 208:06 just bring in the the action we just 208:08 created, which is get my bookings. 208:12 All right. And then right above the 208:15 return, 208:16 let's say const 208:18 bookings and set that to 208:22 await get my bookings. All right. But we 208:26 need to make this function asynchronous. 208:31 All right. And then in the return 208:34 we're going to check for let's see 208:37 bookings 208:40 dot length and say if that's equal to 208:44 zero then 208:46 something else something else. So if 208:50 it's equal to zero have a paragraph and 208:52 let's do text-g gray- 600 and let's do 208:56 margin top four. And in that I'm going 208:59 to say you have no bookings. 209:05 All right. And then else if there are 209:06 bookings then we want to map through 209:10 them. So let's say 209:13 bookings do map 209:17 and pass in 209:19 here booking. 209:23 All right. Uh actually what am I doing? 209:24 I don't need these curly braces here. 209:28 So get rid of that and that. 209:31 And then what I want to show for this 209:33 ultimately is going to be another 209:34 component called booked room card. But 209:36 we don't have that yet. So let's just do 209:38 an H3. 209:40 And before I actually show you before I 209:43 actually put anything in here, I want to 209:44 show you what a booking includes. 209:48 Because remember that we're not getting 209:49 the rooms here. We're getting the 209:51 bookings which is a collection that has 209:52 those four fields. the user ID, the room 209:55 ID, and the dates. So, let's do a 209:58 console.log of bookings. And you guys 210:01 don't have to do this part. 210:03 And if I go and reload the page, 210:07 you can see what that entails. It's an 210:10 array that has the user ID, the checkin 210:13 and checkout, the ID created at 210:15 permissions and stuff. But look at the 210:17 room ID. Since we added a relationship, 210:20 we have access to all the room fields on 210:23 the room ID. So, it's not just this in 210:26 the room ID. I'm sorry, not that, but 210:27 whatever the ID is, it has all this 210:30 stuff. So, we can technically do booking 210:33 down here. We should be able to do 210:35 booking 210:38 dot room 210:41 dot and then any of those fields like 210:43 the name, right? So, let's get rid of 210:46 the console log. 210:48 And I want to try this out. So, we'll 210:50 save. 210:53 And there we go. We have our grand 210:54 conference hall. That's the name of the 210:56 booked room. Okay. Um, but yeah, like I 211:00 said, we we want to have a component 211:02 that we pass the booking into. So, let's 211:05 create that now. We'll go down to 211:06 components and we're going to create a 211:08 new file called booked room card.jsx. 211:15 And let's do SFC 211:19 booked room card. 211:23 Okay. And this is going to take in as a 211:24 prop. It's going to take in the booking. 211:29 And then for the the markup, I'm going 211:31 to get that from the theme files. If we 211:33 go to bookings.html html 211:36 and where it says booking one, this div, 211:39 which ends right here, I'm just going to 211:41 grab that. 211:46 Okay. And then we're going to paste that 211:47 in here. 211:51 And I mean, this is all hardcoded stuff 211:53 for now. Uh, let's just change the 211:55 classes to class name. If I save that, 212:01 oh, we we didn't embed it yet. So, we 212:03 need to bring it into our page. So, 212:05 right here, I'm going to say import 212:07 booked room card. And then instead of an 212:09 H3, 212:11 let's get rid of that. And we're going 212:13 to do booked room card, which needs to 212:16 be passed in a key, which I'll set to 212:20 booking dot money sign ID. And then the 212:25 booking itself, we want to pass that in. 212:30 All right. So if I save that, now it's 212:33 showing the the booking, but it's 212:35 hardcoded, right? It's just training 212:37 room. So remember this booking has a 212:40 room ID field that has all the room 212:42 stuff on it. So what we can do is at the 212:44 top we'll deconstruct room ID from 212:50 booking. However, room ID.name that just 212:53 is kind of weird. So let's rename this 212:55 to room like that. Now we should be able 212:58 to come down here and we should be able 213:01 to do room dot 213:03 uh not ID name. 213:09 And there we go. Grand conference hall. 213:12 So next we want the check in and check 213:14 out. So I'm going to replace this 213:17 with room.in 213:20 check in and then this 213:24 replace that with 213:27 room dot check 213:31 oops check out. 213:34 Okay, let's see what that gives us. And 213:37 it gives us nothing. 213:41 Huh. 213:45 Oh, I'm sorry. This isn't part of the 213:47 room. This is part of the booking. 213:55 And there we go. Now, it's it's not 213:57 formatted very nicely, and I want it 213:59 formatted in a very specific way. So, 214:01 what we're going to do is create a 214:03 helper function. And you could put it 214:05 somewhere else, but I'm going to put it 214:06 right in here. And we'll call this 214:08 format date. Um, so con format date. And 214:12 it's going to take in a date string. 214:19 Okay. And then first thing I'll do is 214:21 let's say const 214:23 date and we'll set that to new date pass 214:28 in date string. 214:31 All right. Then I want to get the month 214:33 name. So we can say const options 214:36 and let's set that to some curly braces 214:40 and we'll say month 214:43 and I want to use short. Okay. because 214:45 you can pass this these options in. So 214:47 I'm just putting it into a variable and 214:50 then const month and we'll set that to 214:53 date and we're going to use two localal 214:56 string. 214:57 Uh yeah, this one right here to local 215:00 string and pass in I'm using 215:03 d- us 215:05 and then I'll pass in the options which 215:09 is the object I just created and then 215:11 the time zone. If you don't set the time 215:13 zone, you might have some issues. Um, 215:15 we're going to use the UTC, the 215:17 universal time zone. So, time zone, set 215:21 that to a string of UTC. 215:26 Uh, what's going on here? 215:30 Don't want that. 215:35 Oh, I forgot my 215:39 There we go. All right. So, that should 215:41 give us the month. Right now underneath 215:44 that I want to get the day number. Let's 215:46 just put a comment here. So get month 215:51 and we'll say get day. So we'll say con 215:55 day and set that to date dot and then 215:59 we're going to use get UTC. 216:03 Uh get UTC date is what we want. 216:08 All right. And then we want to format 216:12 time in UTC 12hour. 216:17 And if you want different formatting, 216:18 you can do that. This is just what I 216:20 prefer. So I'm going to say const and 216:22 let's say time options. Set that to an 216:26 object. And I'm going to set the hour to 216:29 numeric. 216:32 Hour is going to be numeric. And then 216:34 minute 216:36 uh minute also 216:40 numeric 216:41 and we're going to say hour 12 and set 216:45 that to true. 216:47 And then the time zone 216:50 uh is it uppercase C? Yeah, time zone. 216:53 We're going to set that to UTC. 216:57 All right. So now we want to get the 216:58 time. Set that to date dot Whoops. 217:03 date dot 217:05 to local string 217:09 and en- us if you're somewhere else you 217:12 can use that whatever your region is and 217:14 then time options 217:19 all right so time options now we can 217:22 make the let's say final 217:26 formatted string 217:28 so we're going to return from this 217:30 function and I'm just going to use back 217:32 ticks here 217:35 and we want the month. 217:38 So month and then a space and then the 217:42 day and then space at space and then the 217:47 time. 217:50 Okay. So now we should be able to just 217:53 wrap these. So, the booking check-in, I 217:56 should be able to wrap that in format 217:58 date 218:01 and then wrap this as well. 218:06 So, format date. All right, let's try it 218:10 out. 218:11 There we go. October 3rd at 11:00 a.m. 218:14 October 3rd at 100 p.m. So, yeah, I know 218:18 it's kind of a lot, but I mean, I just 218:20 want it formatted a specific way. So now 218:25 the next thing we do the view room we 218:28 want to have that go to the actual room 218:30 not room HTML. So let's bring in the 218:32 link 218:34 uh link component from next. 218:40 And let's set this 218:43 to oops link and the href here. Let's 218:49 put some curly braces and back ticks. 218:52 And it's going to go to slashroom slash 218:54 and then the room dot money sign ID. 219:03 Okay, so that works. Now we want to be 219:06 able to cancel the booking. So we're 219:08 going to have an action for that. 219:12 Uh let's see. So actions and let's call 219:15 this cancelbooking.js. 219:20 So I I I love how this is structured 219:22 with the actions because you can just 219:23 see everything you can do in this 219:26 application. You can just see it right 219:28 here and and just go directly to it and 219:31 you know change it. So it's very I think 219:33 it's structured in a very clean way. Um 219:36 I think it's much cleaner than API 219:38 routes. But that's not to say that API 219:40 routes aren't useful. If you're building 219:42 a service where you want multiple 219:44 frontends, maybe you want a mobile app, 219:47 you want a web app or even something 219:50 even something else, then you'll have a 219:52 nice API to work with if you're using 219:54 API routes. But if you're building a 219:56 monolithic application like this, then I 219:59 think this is the best way to do it. So 220:01 for cancel booking, let's copy again, 220:04 we'll just copy the get my rooms 220:07 because it has most of the imports and 220:09 stuff that we need. So yeah, this 220:12 cookies that's good. Um, we don't need 220:15 query. We can get rid of that. I do want 220:19 revalidate path. So let's import 220:23 uh revalidate path from next cache. And 220:26 then we also want check off. 220:32 And yeah, that should be good. Um, let's 220:35 change this. So instead of get my rooms 220:37 here and here, we want to change that to 220:40 cancel booking. 220:44 And let's just handle the error while 220:46 we're at it. So, we'll just say failed 220:48 to 220:52 cancel 220:53 booking and then 220:56 return 220:58 oops return an object with error 221:05 failed to cancel booking. All right. So, 221:07 let's come back up here. And again, 221:10 we're doing the session thing, checking 221:11 for it. In the try, we're just using 221:14 databases. So, we can get rid of that. 221:17 Passing in this the session into the 221:19 create session client. 221:22 And then, let's see. We want the user 221:24 from 221:28 user 221:30 from await. 221:32 What is it? Check off. 221:37 Check for the user 221:47 and we'll say you must 221:50 you must be logged in to cancel a 221:54 booking. 221:55 All right. So to do do the actual 221:59 cancelling, we have to first get the 222:01 booking. So, I'm just going to get rid 222:03 of this stuff here. 222:06 And let's say get we'll say get the 222:09 booking. 222:11 So, we can do that with 222:15 uh let's see, we're going to say await 222:17 databases 222:21 databases. And then we're going to use 222:22 get document 222:25 which is going to take in the database 222:27 ID and the collection which I'll just 222:29 copy from a different thing here. Yeah. 222:33 So right here. 222:37 Okay. But make sure you change this to 222:39 not the rooms collection, the bookings 222:41 collection. 222:43 So you you see why I use these variables 222:46 because we're using the collection name 222:49 in so many different places. is if we 222:50 were to if that was to be changed then 222:52 we would have to change them in all 222:54 those places. I'd rather just have it 222:56 here so we can change it in in just 222:58 thev. 223:00 Um now let's see the cancel booking is 223:04 actually going to get passed in a 223:06 booking ID 223:08 and that's what we need to pass in as a 223:10 third argument here is booking 223:14 ID. So that will that'll get the booking 223:17 for us. Now we want to check and make 223:20 sure that the booking belongs to that 223:22 user because we don't want just anyone 223:24 to be able to cancel anyone's booking. 223:27 So let's say check if booking 223:33 belongs to 223:36 current user. 223:39 Okay. So to do that we can just have an 223:43 if statement and we can say if the 223:45 booking 223:46 user 223:48 id is not equal to the user do ID from 223:53 the session then we want to return 223:59 error and we'll say 224:02 we'll say you are not authorized 224:06 to cancel this booking. 224:11 All right. Then we want to cancel or 224:14 delete the booking. 224:18 So for that we can await databases 224:22 dot and then we want to use delete 224:24 document. 224:27 And again that's going to get passed in 224:29 both of these things. Actually it's 224:31 going to get passed in all three the 224:33 booking ID as well. 224:37 All right. Then we just want to 224:38 revalidate the path 224:42 of bookings. 224:45 Oops. Should be slashbookings. 224:47 And then 224:50 just pass in layout. 224:53 And then finally, we want to return uh 224:56 we're just going to return success 224:59 true. 225:02 So that's our cancel booking action. 225:03 Now, we want to hook that up and that's 225:05 going to be hooked up in the booked room 225:07 card component. 225:09 All right. So, in order to use it here, 225:13 it has to be a client a client component 225:17 because there's an we're handling 225:18 events. However, I don't want this to be 225:20 a client component. So, what I'll do is 225:22 create a separate component 225:25 and we'll call this 225:27 what do we want to call this? We'll say 225:29 cancel booking. Cancel booking button.j. 225:33 JSX. 225:37 So, cancel 225:39 booking button. 225:44 And let's see, we're going to make this 225:47 client. So, let's say use client. 225:51 And 225:52 this is where we're going to bring in 225:54 the action that we just created. So, 225:56 let's import cancel booking. And then 225:59 I'm also going to import toast. 226:05 And let's see, this is going to get 226:07 passed in the booking ID as a prop. 226:13 And let's copy from the booked room 226:15 card. We want to grab the button. 226:18 So, right here, 226:20 just going to cut that. And then we want 226:23 to put that in right here. 226:26 And before I do anything else, I'm just 226:27 going to save it and bring it into my 226:29 booked card. 226:32 So, import cancel booking button. And 226:35 then 226:37 we're going to put it right here. 226:42 And it needs to know what do we pass in 226:44 the ID, right? Yeah, the booking ID. 226:48 So, we're going to set that to 226:51 booking dot money sign ID. 226:58 Okay. Okay. And it should show us the 227:00 same thing. Now, let's go. Let's add the 227:03 the actual functionality, which 227:06 uh is going to be pretty simple. We're 227:08 just going to add an on click. I don't 227:10 know why there's an href here. Let's get 227:12 rid of that. 227:14 On click. And let's set that to handle. 227:18 Uh what do I want to call this? Let's 227:20 say handle 227:23 cancel click. 227:27 Okay. Okay. And then we're going to add 227:28 that above the return. Let's say const 227:34 handle 227:36 cancel click. 227:38 And that's going to be a sync. 227:43 Okay. And I want to do a confirmation 227:45 since we're deleting something. So we'll 227:48 say if not 227:51 confirm 227:53 and we'll say 227:57 are you sure you want to cancel 228:02 this booking? 228:06 Uh yeah. So are you sure you want to 228:07 cancel this booking? Then we're just 228:10 going to return. 228:13 All right. Else we don't need to put 228:15 else but down here. So let's open up a 228:17 try catch. 228:19 Okay. And then we want to use the 228:21 action. So I'll just have a variable 228:23 called result. Set that to await 228:28 cancel booking. 228:30 And remember that gets passed in the 228:31 booking ID. 228:34 All right. Then we want to check for 228:36 success. So if because remember it 228:39 returns either result dotsuccess or 228:42 error. So I'm just going to check for 228:45 result.uccess. success. 228:48 And if that's true, then we're going to 228:49 toast. 228:53 And say booking 228:56 say booking cancelled successfully. 229:02 And yeah, it should be good. Now in the 229:05 catch, we'll just do a let's do a 229:08 console log 229:11 and just say failed to cancel booking 229:17 along with the error 229:19 and then return 229:25 failed to cancel booking. 229:29 All right, so let's give it a shot. and 229:31 come over here and cancel. I get a 229:34 confirm. Okay, 229:38 booking cancelled successfully. Cool. 229:42 So, hey, things have gone pretty smooth 229:44 here. I was expecting to have to have 229:46 more issues, mistype, misspellings and 229:48 things like that, but yeah, everything's 229:50 been going pretty well. Now, one thing I 229:53 forgot is to add the heading. So, in the 229:55 the bookings page here, I already 229:57 brought the heading in. So, we just want 229:59 to go right at the top here and add in 230:03 the heading with a title. And for the 230:06 title, I'm just going to say my 230:07 bookings. 230:12 There we go. Yeah. So, that looks 230:13 better. So now one very important thing 230:15 we need to implement is checking the 230:18 room availability because if there's a 230:21 room booked from say 1 to 3, if someone 230:24 tries to book it on that day at 2 or any 230:27 time in between 1 and 3, we don't want 230:29 them to be able to book it because then 230:31 it would be double booked. So that's a 230:33 pretty crucial part to building an 230:35 application like this. So, what I'm 230:38 going to do is I'm going to create a new 230:40 action called check room 230:43 availability.js. 230:48 And we can copy over one of these, I 230:51 guess. Get my rooms. We'll just stick 230:54 with that. Copy it. Paste that in 230:57 because there's we're going to pretty 230:59 much use all this stuff here. Yeah, even 231:02 query redirect. Now there is another 231:05 package I want to install called Lux on 231:07 and that will it it allows us to 231:10 manipulate dates and times and I was 231:13 having issues with with comparing the 231:15 dates. It was still letting me book even 231:17 if there was an overlap until I I used 231:20 this Luxon package. So we're going to go 231:22 ahead and install that. Let's jump down 231:24 here and say npm install and it's luxo 231:27 n. 231:29 And then in this action, we're going to 231:32 import 231:34 the date time object from Luxon. 231:39 Okay. And then let's move down into what 231:42 we want. Obviously, we want to change 231:44 this and this to 231:47 check room availability. 231:53 And we want to do this. So get the 231:54 session cookie and so on. Um, in the try 231:57 catch, we just need databases. We don't 232:00 need account. So, we'll get rid of that. 232:04 And then, let's see. Uh, I'm just going 232:08 to get rid of the rest of this that's in 232:09 the try for now. And then in the in the 232:13 catch, we'll just say failed to I guess 232:16 failed to check 232:19 availability. And then we'll do a return 232:25 with an error and say the same thing. 232:33 All right. Now, up here in the try under 232:36 where we we're getting the database 232:38 instance, we want to fetch all bookings 232:43 for a given room. 232:47 So to do that, I'm just going to copy 232:49 from one of these other ones like get my 232:51 rooms. So this right here, just copy 232:54 that block of code 232:56 and paste that in. And then I'm going to 232:58 change rooms to bookings. 233:02 And let's see, 233:05 we want to change the the collection. 233:07 It's not the rooms collection that we're 233:09 looking for. It's bookings. So we want 233:10 to change that. And then for the query, 233:13 instead of matching the user ID, we're 233:15 matching where the room ID matches 233:20 matches room ID. Okay? And that room ID 233:24 is going to come in through here as a as 233:28 an argument. So let's say room ID. And 233:32 then it's also going to take check in 233:34 and check out. Okay. Okay, so that's 233:38 this represents what we put in the form 233:40 for the check-in and check out. 233:43 All right, so back down here under where 233:45 we fetch all the all the bookings, we 233:48 then want to loop through. Let's say 233:50 loop over 233:52 bookings 233:54 and check for overlaps. 233:58 So let's do four. We're going to use a 234:00 for of loop. So for const 234:04 booking of 234:07 bookings 234:09 and we want to get the the check-in date 234:14 time and the checkout date time. So 234:16 that's going to be 234:19 uh let's see booking 234:22 check in date time 234:26 and that's going to be set to uh now 234:31 we're going to take the booking.checkin 234:34 check_in remember that's the name of the 234:37 attribute or the field but this is where 234:40 luxon comes in. We want to wrap this in 234:43 uh a function that converts it to a UTC 234:47 date time. So up here above the check 234:51 room availability function, I'm going to 234:53 create a new function 234:56 and we're going to call this 2 UTC 235:00 date time. Okay. And it's going to take 235:03 in a date string. 235:08 All right. And what we want to do here 235:10 is um pretty simple. We're just going to 235:12 return and we're going to use that 235:13 datetime o object from luxon that I 235:16 brought in and there's a method called 235:18 from ISO. So from ISO and then that's 235:23 going to take in the date string that we 235:25 pass in. And then some options and in 235:28 those options I'm going to say zone and 235:30 we're going to use UTC. 235:33 And then we just want to add on to this 235:35 the method of to UTC like that. Okay. 235:39 And then we can just come back down 235:41 here. Actually, let me put a comment 235:43 there. So, this will convert a date 235:47 string 235:48 to a Luxon 235:52 datetime 235:53 object in UTC. 235:57 All right. And I I needed to do this in 236:00 order for the matching to work. It 236:01 wasn't. There was something to do with 236:03 the time zones. I I hate dealing with 236:06 times and dates and times and time zones 236:08 in programming. So, let's wrap this in 236:11 two UTC date time like that. And then 236:15 we're going to copy this down. And we 236:17 want to do the same for the check out. 236:19 So, this will be booking checkout date 236:22 time. 236:24 All right. So, now we have those. Um, 236:26 next thing we're going to do is check 236:28 for an overlap. So I'm going to create 236:31 another function to check for the 236:33 overlap as well. Um so basically we want 236:37 to match the this check in and this 236:40 check out with this check in and this 236:42 check out. Um but we also want to wrap 236:47 these in the the two two UTC date time 236:52 function. So why don't we do that um 236:55 right here above where we fetch the 236:58 data. So we'll say const and I'll call 237:00 this check in 237:03 date time. Set that to 2 UTC 237:09 date time. And then we're going to pass 237:12 in check in which comes in as a as an 237:15 argument. And then same thing with the 237:16 check out. 237:20 Okay. So we're just we're just getting 237:23 the all the date times in the same exact 237:25 format and same exact time zone. So, 237:28 we're basically going to match these to 237:30 these, but I'm going to create a 237:32 separate function that does that just to 237:34 kind of keep it clean. So, up above the 237:37 check room availability, let's say um 237:40 we'll say check 237:43 uh what do we want to say? Check for 237:47 overlapping 237:49 or overlapping date ranges 237:52 using 237:54 um I guess no we'll just say that. 237:58 So function 238:01 um what do we want to call this? We're 238:02 going to call it date ranges 238:05 overlap. 238:07 Okay. And it's going to take in four 238:10 arguments. It's going to take in check 238:12 in A 238:15 and check out A 238:19 and then check in B 238:23 and check out B. 238:27 All right. And then what we want to do 238:28 is just returns a single line. We're 238:31 gonna say if check in A is less than 238:35 check out B and 238:39 check 238:41 out A is greater than check in B. Okay, 238:46 so just look at that, you know, make 238:49 sure that you have the same exact thing. 238:50 And I know it's a little confusing, but 238:52 let me try to just explain it a little 238:54 bit. So let's say we the check-in A and 238:58 checkout A represent the form the dates 239:00 that we're looking to to date to uh 239:02 book, right? So let's say we want to 239:05 book from 1, 239:07 yeah, we'll say from 1:00 to 239:11 uh and then the other A is the check 239:13 out. So 1:00 to 3:00, right? And then 239:16 the B represents what's already in 239:19 there. So, let's say there's a booking 239:21 for um two, right? So, there's a booking 239:25 in there for two to let's say five. 239:30 So, what we're saying here is check in 239:32 A, which is 1:00, 239:34 um is if that's less than check out B, I 239:37 don't know why I put C here. So, if 239:40 that's less than than B, so is 1:00 less 239:43 than 5:00, which it is. Now that on its 239:47 own doesn't mean that it's there's an 239:50 overlap because you could have like this 239:52 could be 1 to uh 130 right so if this 239:56 was 1 to 130 this is still true right so 240:00 a is still less than b however this 240:03 isn't within this range so that's why we 240:06 have this right side where we're seeing 240:08 if check out a if this is greater than 240:12 check in b and in this case it's not so 240:16 it's fine but if we have this at you 240:18 know 3:00 then yes then this is greater 240:23 than that and that that means there's an 240:24 overlap. All right so what's happening 240:28 here is if this is true then that means 240:30 there's an overlap and we can't do the 240:32 booking. 240:34 So now let's come down here and let's 240:36 pass in let's run that function within 240:38 our loop and we're running it within the 240:40 loop because we're doing it on every 240:41 single booking for that room. All right. 240:44 So let's say if 240:47 and date ranges overlap and then we're 240:51 going to pass in um a few things. We're 240:54 going to pass in 240:57 where is it? These two, right? So we get 240:59 check in date time, check out datetime. 241:02 So that and then I'll just go ahead and 241:04 copy that down and change that to out. 241:07 So those are the that represents the 241:09 dates we're looking for. And then these 241:11 two represent the dates in the database 241:13 already. So that and then check out. 241:19 And if this is true, then that means 241:22 there is an overlap. So I'm going to 241:24 return false from here. All right? 241:26 Because ultimately we're saying we're 241:28 going to use this this whole function 241:30 here check room availability by saying 241:32 if check room availability 241:35 then if it's true then book, right? If 241:39 it's false then don't. So obviously we 241:41 want to return false if there is an 241:43 overlap. In fact they'll put let's say 241:46 uh overlap 241:47 overlap found do not book. 241:52 All right. Now if this is not true that 241:54 means there's not an overlap. So we're 241:56 going to keep going and we're going to 241:57 go outside of the um actually yeah we 242:01 want to be 242:03 we want to be outside of the loop here. 242:06 Still within the try. And let's say no 242:09 overlap found 242:12 continue to book. So we just want to 242:15 return true. 242:20 And yeah, that should do it. So I'm 242:23 going to save this file. And where we 242:25 want to use this is going to be in the 242:27 bookroom action. So let's open that up 242:31 and let's bring that action in. So, I'm 242:35 going to say import check room 242:38 availability. 242:41 And let's see, we're going to use this 242:44 right before where we create the booking 242:46 data. So, right here, let's say check if 242:49 room is 242:52 available. 242:53 And I'm going to create a variable 242:55 called is available. And I'm going to 242:58 set that equal to await. and then check 243:03 uh check check room availability. 243:07 Okay. And then we want to pass in 243:10 we want to pass in the remember the room 243:13 ID. So right here, 243:16 where is it? Uh right here. The room ID, 243:20 the check-in, and the checkout. Now the 243:23 room ID, we don't have that in here yet, 243:25 but that's coming in through the form. 243:28 Actually, we have it right here. So, I 243:30 could just put that in there, but since 243:32 I'm using it in two places now, why 243:34 don't we just create a variable? So, 243:37 we'll say const room ID and set that to 243:42 that. And then we can just use 243:46 uh room. Actually, I don't want to use 243:48 an underscore stick to convention here. 243:52 So, like that. And then we're passing in 243:54 room ID. And then we want to pass in 243:56 room ID here. And then for the for the 243:59 dates, it's going to be these two, 244:02 right? Check in date time and check out 244:04 date time. So check in and check out. 244:11 And that will return true if there's no 244:15 overlap. It'll return false if there is 244:18 an overlap. So let's say if 244:23 uh not is available. So, if there's an 244:26 overlap, then I'm just going to return 244:29 an object with an error. And we're going 244:32 to say, 244:34 what do we want to say here? This room, 244:37 this room is already 244:40 booked for 244:43 the selected time. 244:48 And that should work. So, let's try it 244:50 out. Let's go to rooms. Go to the grand 244:52 conference hall. I'm going to choose 244:54 tomorrow 244:56 and I'm going to choose 1, 245:00 let's see, 1:00 p.m. to 245:04 3:00 p.m. 245:08 And I'm going to book that. And that 245:09 should let me Okay, so that let me 245:12 because there were no other bookings. 245:14 Now, let's try it again. I'm going to go 245:15 back to the same room and I'm going to 245:17 try a date that does overlap or I'm 245:21 sorry, a time. So two and a date of 245:24 course. Um so let's do 2 2:00 245:30 p.m. to let's say 5:00 p.m. 245:36 So this shouldn't let me right because 245:39 the other one is between 1 and 3 and 245:41 obviously two is in that there's an 245:43 overlap. So let's try it out. And it 245:45 doesn't let me. Now, if I were to make 245:47 this 301, it should let me because it 245:52 ends at 3, the other one. So, let's try 245:55 that. 245:56 And it lets me. Okay. So, you can see 245:59 this one ends at 3 and this one starts 246:02 at 3:01. 246:04 And of course, if you wanted to change 246:05 the logic, you could. So, maybe if there 246:07 was, you know, you maybe you want to 246:09 have an hour break to clean the room or 246:11 something like that, but um we don't 246:13 need to to get into that. 246:16 So yeah, and I would say that's one of 246:17 the most important parts of a project 246:20 like this is you you don't want people 246:22 to be able to to double book. All right, 246:25 so that's our application. So it's now 246:28 complete and now what we're going to do 246:30 is deploy it to Verscell. So I'm going 246:32 to just go ahead and sign out and I'm 246:35 going to close that up and just close 246:39 these up. Now you're going to want to 246:41 push to GitHub. Okay, I've already done 246:44 that. So, if I go to my repositories, 246:49 I have my Booket app right here. It's 246:51 private now, but it will be public for 246:53 you guys by the time you watch this. Um, 246:55 so you do need to get it on GitHub. And 246:57 I'm sure most of you guys know how to do 246:59 that. But if not, just make sure you 247:01 have Git installed. Just check make sure 247:05 you have it installed and then just run 247:07 git and in the root of your application. 247:10 That'll initialize your repository. Then 247:13 you can get add all that will add 247:16 everything to the staging area. Then you 247:18 can get commit-m 247:20 and then a a comment like initial commit 247:24 and that will commit to your local 247:26 repository. Then you want to go to 247:28 GitHub, click the plus sign, create a 247:31 new repository, and it'll give you a git 247:34 remote command, and you just run that. 247:37 That'll add it as your remote repository 247:39 and then get push will push it to 247:42 GitHub. Okay. Once you have it there, we 247:44 can then go to Verscell. 247:48 So, verscell.com 247:51 and 247:52 the free this is the pro account I 247:54 think. Yeah. So, um the free account is 247:57 very generous and you'll be able to do 247:59 everything free without any credit card 248:01 information or anything. and then add 248:03 new project. 248:05 And then you can just find your project. 248:07 Mine's right here, the first one. So I'm 248:09 going to click import 248:12 and configure project. Now the 248:14 environment variables. You're going to 248:16 want to grab all of these from the 248:19 theenv local. And it's a cool feature 248:23 that you can just copy this the whole 248:26 thing. Click in the first one, paste, 248:28 and it'll add all of them for you. Now, 248:31 the public URL right now, we don't have 248:34 the Verscell domain that they're going 248:36 to give us because it it gets set up 248:38 after we deploy. So, I'm going to leave 248:40 this at local host, but I'll come in and 248:42 change it after. But the rest of this 248:44 stuff, yeah, that's all going to stay 248:45 the same, your app, right key, and all 248:47 that. And then we can go ahead and 248:49 deploy. 249:45 All right. So now we're deployed and we 249:48 can uh first I'm going to continue to 249:50 the dashboard and if I click on this it 249:53 should open a new tab with the 249:54 application. Now this is the the current 249:57 domain that Versell gives us and of 249:59 course you can add your own domain that 250:01 you buy like bookit.com or whatever. I'm 250:04 sure that's taken. But let's take this 250:06 URL now and just go into 250:10 uh settings and then environment 250:13 variables. And I'm going to change 250:16 where is it? The public URL, I'm going 250:18 to edit that. And instead of local host, 250:21 I'm going to put this in here and save 250:23 it. All right. Now, you can kind of go 250:27 around and and test things out. So for 250:30 instance, let's register a new account 250:33 and and the same it's the same apprite 250:35 account, right? I'm using the same API 250:37 key. So I don't have to change anything 250:39 there. Any users that I registered 250:41 locally are still going to be able to to 250:44 get logged in here because it's the same 250:46 account. But let's try this out. We'll 250:49 do uh I don't know, Harry. 250:51 Harry White 250:54 Harry atgmail. 251:02 All right. So, we'll log in. 251:11 All right. So, we're able to create an 251:13 account. Log in. I should have no rooms 251:15 obviously because it's a new account. I 251:17 should have no bookings. 251:19 And let's try to create a room. So, I'm 251:22 just going to add room. Add a bunch of 251:25 dummy data here. We'll say Harry's 251:29 room. 251:31 Grab an image. 251:34 Save. 251:37 And there it is. Harry's room. And I 251:40 should see that in my room listings 251:43 here. Okay. Now, let's try a booking. 251:46 So, I'll book the grant. Uh, this right 251:48 here. 251:52 And I still have that booking from what 251:54 was it? One to to three or whatever. So, 251:57 if I try to book within that time range, 251:59 it still shouldn't let me. Okay. So, 252:02 it's not letting me. But if I change 252:04 this to something like let's do 439 252:08 to 539. 252:12 Uh, oh, I I booked another one, too. 252:15 That's right. So, let's do like a 252:21 All right, there we go. So, it's it's 252:23 deployed. And of course, you could add 252:25 your your own domain name. I'm not going 252:27 to do that. Um, but yeah, hopefully you 252:30 guys enjoyed this. I know this was a 252:32 freaking long video. And congratulations 252:35 if you actually f, you know, followed 252:36 through the whole thing and and built 252:38 the application. I hope you learned a 252:40 lot and thanks for watching.