Fullstack Notion Clone: Next.js 13, React, Convex, Tailwind | Full Course 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey there my name is Antonio and welcome to the newest video on my channel in this tutorial you're going to learn how to build a beautiful notion clone which we are going to call Jan as you can see we're going to have a beautiful landing page with a knv bar with a footer and some nice images right here we're also going to have a toggle of light and dark mode which is going to reflect the entire application as well so without further Ado let's see exactly what this application will be capable of so I'm going to go ahead and click on get Jan free right here and I'm going to sign it using my GitHub once I authorize the sign in I'm going to get redirected back to the landing page and you can see now it says enter Jan once I'm inside of here we have a nice message welcome to Antonio's Jan and we have a big button to create our first note so let's go ahead and do exactly that as you can see it's created in real real time it's added to the sidebar right here and guess what we can rename it in real time as you can see it's been renamed right here in the toolbar above as well as in the file explorer on the left and I can also do it from here just like that but now it's looking a little Bland so let me rename this to my first note and let me add a little icon to it for example let's use this rocket right here as you can see this has immediately been reflected it here in the Novar and right here in the file explorer I can also add a cover image using Edge store so let me add a beautiful image here from unsplash and once it's loaded you can see how it takes a nice little space here from here I can change the cover or I can completely remove it besides the icon and the cover image we're going to have a notion style editor which means we can write commands with a slash I can select the heading and I can write subtitle like that I can also go ahead and select a smaller heading and write a description or I can just write regular text besides text I can also embed an image in the editor itself I can either use the embed by URL option or I can upload an any any image I want so let me pick another image from unsplash right here and as you can see I can also resize it so it fits exactly what I need besides text and images I can also answer their bullet list like this I can also select a specific item I can make it bold italic I can underline it or I can strike through it I can also give it a background color like this besides bullet list we also have numbered lists one two and three like that now let's explore what else can we do with this application as you can see in the sidebar right here I have a little plus icon which means that I can add a children in inside of this note and I can as well rename it in real time perfect and I can go ahead and delete it as well and we can do that as many times as we want now you might be thinking well with all of this infinite children at some point we're going to run out of space here right well don't worry just like in real notion we can expand the Side Bar and make it fit our content so we have room in our file explorer now you might might be wondering what these three buttons do well I can select and delete the entire note and all of its children and if I take a look at my trash right here you can see that all of that is here I can now go ahead and filter by my first note and you can see that I can go ahead and click on that note this is the first note that we created and now it has a banner saying this page is in trash I have an option to restore the entire page or to delete it forever so first let's try restoring the page and as you can see this is a smart restore meaning that it also restored everything else in the trash which was uh children of this note beautiful but what happens if I delete partially like this well as you can see only that part has been deleted and if I go ahead and randomly revert these items back you can see that they become Standalone items because they no longer have a reverted parent so we have a very smart implementation here besides this we can also collapse the entire sidebar like that we can also publish the note so I'm going to pick this first note which we created and I'm going to click publish right here so right now our work is private and only we can see it but once we publish the note it's publicly visible on the web so I'm going to copy this link right here and I'm going to go ahead and paste it in incognito mode right here and as you can see I can now preview this note as a guest but I cannot edit the icon the cover image the subtitle nothing I can just look at this and let's see what happens once I click on publish as you can see now it says an error something went wrong and we can go back to the landing page as I guessed so we're going to have beautiful error messages we're going to have loading skeletons and much more and just as I've shown you on the landing page we can click on settings right here and we can change this to dark mode as well and it fits beautifully in this application but just right now I'm going to change it back to light mode I can also search through all of my documents I can find my first note and I can click on it I can also use a shortcut command key like this and I can then go ahead and find another note and enter it just like this so that's going to be our project I think this is a very exciting project we have a lot of new technologies to learn and a lot to do but we are not done here this entire application is also going to be fully responsive as you can see right right here I'm uh on a mobile mode here and I have a collapsible Navar and from here I can switch to my first note like that I can open popups I can do everything I want from here I can visit the trash I can revert notes back and I can visit them again beautiful so I hope you're excited for this project because I sure am and without further Ado let's get started so let's get started by configuring our next3 project so here on the left side I have my visual studio code and I've prepared my terminal you can use any terminal you want if you want to access the one in Visual Studio code like I have I pressed on this little problems tab right here and then I pressed on the terminal option like that so the command I'm going to run is npx create-- apppp at latest meaning that we're going to use whatever is the latest version of the create next app and then we're going to give our project a name in my case that's going to be be a notion dclone and you can just go ahead and press enter it's asking us whether we want to use typescript for this tutorial I'm going to use typescript so you can select yes for the Sint option you can select yes as well for Tailwind CSS select yes as well Source directory leave it at no or select no yourself because we're not going to use the source directory in this tutorial for the app rowter this is very important make sure you select yes because we're going to be working with the new app router here and we're not going to be working with the pages folder as in the previous versions of nex1 13 so make sure you select yes for this option it's asking us whether we want to customize the default import alas so the default import alas is going to be an alas inside of our Imports so that we don't have to write all of those long relative Imports if we are in a deeply nested folder you're going to see that in action once we start tutorial but what I recommend you choose for this option is no so leave it as it is the default one is the add sign and we're going to be using that in this tutorial so just select no for this option and there we go you can sit back relax and wait for this to install great so now that we have our next project configured what I want to do is I want to open this folder where it says that it created the notion clone project so I'm going to go ahead and find the open button right here and I'm going to select notion clone because that's the name of my project if you get this prompt feel free to just say yes I trust the authors like this great so just make sure you're inside of your new notion clone to confirm that You' installed it correctly you should have the app folder with the favicon with the globals layout and page. ESX you should also have the public folder the node module folders some slin configuration and a a tailwind and typescript configuration because we chose those options initially when we created the project what I want to now is initialize a Shaden UI inside of the project that is a library which is built on top of a radic UI and we're going to use it to style our entire project and it works very well with Tailwind so it's perfect for our use case so let's head back inside of our terminal right here and I'm just getting a little update here so let me pause the screen and update my shell you don't have to do this of course all right sorry for that and now you can just go ahead and write npx shat cn- at latest in it like this which is short for initialize and go ahead and press enter and now it's asking us a couple of questions so let's look at the first question it's asking us whether we are using typescript remember we initialized our entire project with with typescript that means we also want typescript components so select yes for this one for the style go ahead and select default for the color it doesn't really matter but if you wanted to look the closest to my tutorial go ahead and choose neutral you can of course play around and select your own dark color but neutral in my case kind of gives me the best looking app similar to notion so select neutral for this option now it's asking where is your global. CSS file well just a moment ago we opened the app folder and we confirmed that it is there so you can just press enter now it's asking us if we want to use CSS variables for colors and you can freely select yes for that option now this is a tricky part so in my case it's asking me where is your Tailwind doc config.js located but let's take a look in my browser right here so don't close the terminal I'm taking a look at my browser here and notice that I don't have Tailwind doc config.js instead I have TS so if you have the same thing if you have a file called Tailwind doc config dots instead of JS then you need to modify this command so all that it's important is that it doesn't create two Tailwind files for you don't worry if you mess this up you can still work and your project will work and your Styles will work and your Tailwind will work even with two different config files but let's do this properly so go in your file browser and confirm whether you have a DOT typescript or a JavaScript version of the Tailwind config then go back inside and what you can do here is you can press the Tab Key and then you can manually edit from JS to TS or if you actually have ajs file you can just leave it at JS in my case I need a TS file so I'm going to change it tots and press enter remember I pressed the tab button to edit this placeholder which was hardcoded in this interface go ahead and press enter now it's asking us to configure the import alas for components remember when we configured our project we set the default import Alias to be an add sign meaning that this placeholder is going to work so we can safely press enter here same is true for our lib and utils and it's asking us what we are using server components that is referring to the question in next 13 initialization where we selected the app router since we selected yes there that means that we're also selecting yes here and go ahead and confirm this configuration by pressing enter or the y letter and now just sit back relax and wait for this to finish and now that you finally set up the shaten UI you can go ahead and run mpm run Dev inside of this repository and that is going to start the nextjs project on Local Host 3000 so I'm going to go ahead in my browser now and I'm going to refresh this screen and you should be seeing something similar to this great so we're now going to modify this so it's actually more suitable for the project that we need so let's go ahead inside of our app folder and let's see what we have here so first we have the layout. DSX file so layout files are kind of like group files which reflect the entire project they can be Global like this one which is reflecting every single route and every single component inside of our nextjs project or they can also be grouped inside a specific folder but we're going to come to that a bit later so layout is not the file which you would technically use to render something of course you can but it's more of a file which you would where you would put for example a reusable layout like a sidebar and a navigation bar right whereas the page. DSX would be the one where you want your user to do something like a form or something like that so first thing I want to do is go inside of page. TSX file right here and I want to remove everything inside of this return function so let's go ahead and remove everything here and let's go ahead and write a paragraph saying hello notion clone and save this and now you should get all of that removed and instead you're going to have a nice little text probably much smaller for you so I just zoomed in a lot saying hello notion clone and now what I want to do is I want to try out our CSS right so one thing that I want to show you is that I'm using an extension called Tailwind CSS so I'm going to write Tailwind here and there it is it's the first option right here and let me just close this so it's the first option here and it's called Tailwind CSS intellisense make sure you install this package because writing Tailwind is going to be much much easier with that for example you can now open a class name here and you can see that when I write font for example well not always but it's very helpful when it comes to colors for example text- r-500 like this and you can see that now I have a little box here showing what color the text should be all thanks to that extension I can also do a font Dash and there we go now I I told you sometimes it works sometimes it doesn't you don't even have to use this out complete but sometimes it's very helpful when you want to confirm that the class you're writing actually exists so let's go ahead and let's select font bold like this and save and there we go you can see how in my uh screen on the right here I have Hello notion clone in bold red color and with this extension you can also hover over to confirm the CSS is actually what you think it is inside of that uh class name great excellent one more thing I want to show you is how to install a shaten UI package so for example let's say we want a button component from the shat cnii package all you have to do is go back inside of your terminal and for now I'm just going to shut down this Local Host 3000 and I'm going to write npx shaten shn n-i at latest and I'm going to go ahead and just expand this even a bit more and I'm going to write add button like this and go ahead and just press enter and that is going to install the button component inside of your project so you can now mpm run Dev your project again and let me just zoom back in and now if you take a look at your components folder you're going to notice that you have a new folder called UI and inside of it a button DSX and in here you have a production ready uh accessibility compatible uh button component all thanks to shatan UI and the best part of it all it is fully customizable so you have a lot of useful variants here like a destructive variant of it outline variant of it just like in any other component Library which you would use but what shatan offers us is to go directly inside of the source code and change it to our liking so let's try that out go back inside of page. TSX where you wrote the hello notion clone and for now you can remove this paragraph here and instead open a div and now you can go ahead and add the button from add/ components UI button like this and you can remove this image import we no longer need it so just add a button from add/ components UI button of course make sure that you confirm that the button exists and that you run that shatan command which we did just a moment ago and now you can probably notice that my Local Host did not hot reload why is that happening well a little tip for you every time you shut down your terminal like I did in order to install a button with this command and you then run it again make sure you refresh your Local Host because the hot reload was broken for a second great and as you can see we have a little button here so what I'm going to do now is I'm not going to do a self closing tag instead I'm going to write click me like this and below that I'm going to end the button and there we go now we have a nice button which says click me and if you saw the source code and we just did a moment ago together you know that you can give it a variant prop and you can for example mark it as destructive right this would be something like trash or delete something like that a destructive icon right or you can also make it outline like this whoops and you can see that when you write something that doesn't exist you get a typescript error so let's actually fix it and write outline there we go now you can see a brief little border around it or it can be secondary like that and now you can see it's not as noticeable perfect so we have a very nice button which we can easily customize to fit our notion clone besides this we can also give it a size like a default icon large small stuff like that and the best part of it all is that we can always directly go inside of button like this so either uh the way I did this is I hold the control or the command key and then I press on the component that I want to visit and then I have a little popup here showing me the definitions of that and inside of here there is a button. TSX file and I can click here and that's going to open that file directly or if you want to do it easier you can just visit the components folder UI and find the button so remember we can always visit this component file ourselves and we can change whatever we want want for example you don't have to do this I just want to show you how easy it is to add a new variant let's say we want a variant that our button is purple for whatever reason so we can add that purple I'm going to write text- white BG Das Indigo -500 like that and then I can go back here and guess what I can now give my button a variant of purple like this and look at it it's purple now and I can reuse this as many times as I want but one cool thing that I can also do if it's not something I want to reuse if it is just a onetime thing I can always pass a class name with BG Das Emerald for example 500 and text- white so you can see now I did not pass a variant because this is only going to be one time right we're not going to reuse this perfect so that's why I like shats and so much because it fits so well with tailwind and because it allows us to customize it all the way way to how rounded the borders are directly in the source code so if you added this purple the same way I did you can now safely remove it because we are not going to need it and you can just leave the component as it is perfect and we're going to leave it like this for now in the next part we're going to go ahead and further explore the nextjs structure excellent job so now that we've set up our project I want to show you a cool little extension that is able to go through your entire project and all the files and decide what lters you need to ensure the best code quality and standards in your project so let's take a look right now in this component if I add a random space here nothing is happening well this is technically called a trailing space and if I had a linter like get div check or prettier or something like that I'm most likely to get a little little underlined red error here saying that I have a trailing space here that I don't need and you're going to see this in most Real World Companies the companies you're going to work in in the future or maybe you are already working at usually in my projects I don't Focus so much on lters but it is good to know that there are a practice that should be used inside of your projects so what I'm going to do next is completely optional but I think it's a very very useful extension which can help you improve the quality of your code and gain a better understanding of which linters you need for your project you can maybe even recommend it inside of your company if you're having trouble deciding between some so I'm going to save this like this for now remember I added a little space here just a trailing space and what I'm going to do is I'm going to go inside of my extensions here and I'm going to search for an extension called trunk go ahead and select the first one it's it's called trunk check and go ahead and click install once you've installed it leave it like this and you're going to have a terminal popup this terminal is right now looking through your entire project and all the files and it added all the necessary linters inside of it config file for your project so after it's done you're going to get redirected to this trunk. yaml file or the config file and you can see that you can press any key to close the terminal so just go ahead and press any key to close it let's take a look at this trunk yaml file as you can see it scanned throughout our repository and it decided that we need two runtimes node and python it also decided that we need this linters to ensure the code uh quality and standard of the project and if you want to you can even go further ahead with this and you can go back inside of your terminal I'm going to open a new terminal for now and since you have trunk installed what you can do is you can write trunk login like this and this is going to give you a login link and you can then open that inside of your browser and if you want to you can continue with GitHub after that you might have seen a little authorized button here so just press on it and you're going to have a message you successfully signed into trunk CLI of course this is not required for this project right and you don't even have to log in to use trunk so all that you need is is an extension but I want to show you the website because if you want you can visit the documentation and in here you can read about everything the trunk can do regarding this check linter so it has a lot more options CI debugger CR analytics even some merge queue right here but we're only going to focus on the check uh functionality of trunk and as you can see trunk check runs more than 80 Tools in your repositories which allow you to prevent anti patterns and bugs prevent misconfiguration or infrastructure code and enforce code formatting and style and much more and you can follow this documentation if you want to add it to your repository and you can configure trunk. yaml to whatever you want so let me show you how this now looks inside of the code so if you want to you can play around with trunk you can enable them uh inside of your repository and you can even add GitHub actions which are going to fire on every pool request to ensure that it can be merged according to the code standard and quality to prevent any buck so this is really good for production uh ready websites if you want them perfect so now that I've explained that I'm going to close these tabs right here and I'm going to go back inside of my page. vsx file and as you can see now I have an error here trailing whes space exactly what I told you before so in order to fix this I can just go ahead and remove that and save it like that and now that I installed the package trunk sorry the extension trunk you can see that I also have it right here in my sidebar in your case it might be on this side and if you can't see it you might have three little dots here which will allow you to see hidden extensions so when you click on this here you can also look that you have some hidden issues here like incorrect formatting you can also play around with actions and run the following actions if you want I'm not going to do all of that for this tutorial but I just want to show you how cool this extension is so let's see what incorrect formatting we actually have here and how we can format it well what we can do is run command shift p or control shift B and run format document width like that and select trunk check and as you can see now it added double quotes and it added semicolons at the end of my functions and now if I save this file and go back you can see that it has zero issues inside so you can can use trunk every now and then just to see the quality of your code and if there is anything you can improve in your production code perfect so you can save that now and what I want to talk about now is how routing Works in nextjs so let's go inside of this app folder here and let's create a new folder called test like that and inside let's create a new page called page. TSX and let's go ahead and this is what you're going to see me do a lot of times in the project I'm going to write sfc I'm going to press Tab and all of a sudden I'm going to have this code snippet so if you're wondering how I have that it's another extension called Simple react Snippets so go ahead and install simple react Snippets if you want that so let me show you again all I do is write sfc and press Tab and then I can give my component name test page then I can press tab again and now I am focused on the props but we're not going to need them and if I press tab one more time I'm focused on the return function and inside I can just write a div and I can write hello test page like that perfect and you might be wondering all right so why did they do this well we are learning how routing Works in next 13 so right now I created a test folder inside of the app folder and the test folder has a page. DSX file inside of it that means that that page is accessible now so I can go to slash test now and as you can see I'm going to see hello test page right here so that is how routing Works in next 13 and one trick I immediately want to warn you of every folder that you create inside of the app folder can potentially become a route for example let's say that you want to create a very simple components folder right like we like the one we have out side of the app folder but let's say for any reason you want to create it inside technically you can do that but watch what happens if inside you accidentally have a a component called page so I'm going to do a very same thing I'm going to write component so some component thing like that and I'm just going to a div this is supposed to be a component right so if I created a components folder obviously this is my page component right what well guess what you can technically go to SL components now uh like this and look at this it's rendered as a route that's not something you want to happen so now you learn that every folder you create inside of the components folder is going to become a route so here are a couple of ways to prevent that so we're going to learn some new types of folder inside of the app folder if you want this to not be routable what you can do is rename this to underscore components like this and as you can see now I have a 404 page here right so for now we can delete this underscore components folder like that and if you have this error right here in page. CS that is just cache so you can just save this file and close it all right and now I want to show you something else so I'm going to go back here to the test page so go to slash test here and I want to show you how to create uh route groups so route groups are another type of folder which you can write inside of the app folder which will have no effect on the URL and Route folders are for example parentheses like root for example as you can see I created a new folder inside but if I create so what I'm actually going to do is I'm going to move this main page. DSX well we have the delete button and I'm going to move it inside of root right here and I'm going to save the file like that and if I go back to Local Host 3000 you can see that I still have that page as my root page so why is this working and what happens if I go to slash root for example as you can see it's 404 it's not found that's because the root folder is an organizational folder meaning that it will not affect the URL but one thing that it can do is have its own layout which can then affect all the other routes inside of that component so I'm going to keep this as it is now and I'm going to delete this test page for example and I want to show you for example let's create a new organizational folder called out like this and inside of it I'm going to create another organizational folder called routes and then inside I'm going to create two routes they're going to be login and they're going to be register like this I'm going to create page. vsx in both of them so this is just going to be login page and let me just return a div saying login page like that I'm going to copy that and I'm going to create a new page inside of register I'm going to paste it here and rename it to register page and now how do you think we access this routes so we have login and register well remember what I just explained you with the root folder so when folder is inside of parenthesis inside of the app folder that means it has no effect on the URL it is purely organizational right so the way we can access those routes now are simply by writing slash login like this or slash register like this so you're probably wondering all right well what's the difference between the underscore folder and the parenthesis folder well the underscore folder completely eliminates the entire entire route and all of its content from the router but the organizational folder with parenthesis is just not part of the URL until it reaches a normal folder and you might be wondering well why did I create two organizational folders here well because that's what I'm trying to do I'm trying to organize my f my structure in a better way because this organizational folder can also have another file inside of it called layout. TSX like this and as you can see now I have an error here so let's quickly fix by the error by going inside of layout. vsx and let's write root layout and now let's extract the children from the props because every layout component has children and just mark it as react. react node like this and inside write a div and render children inside and once you save the file you're probably going to notice well our register page looks the same same my login page also looks the same what's the difference well nothing but if you take a look at the HTML structure you're going to notice that we now have one extra HTML right and to make this more noticeable let's give this div a class name of h- full BG D r-500 text- white like this there we go look at now so as you can see now my login page has a red background and it has white text and same is true for my register page why is that happening well that's why organizational folders are useful so a you can use them to organize your files in a better way if you don't want them to just be thrown around and B you can create layouts inside of organizational folders and this layout is going to reflect all the routes inside so both login and register now have a BG red and text white so we are going to use this throughout the project to create separate layouts for example when we uh when we do a landing page we don't want to have a Navar and a sidebar right but when we are inside of our notion clone we want to have a sidebar and a nov bar if we open the specific document well we can leverage organizational routes and layout files in order to achieve that great so we have now prepared this little structure here you don't have to worry about this too much we're going to remove these folders but I just want to practice with you a little bit so what I encourage you to do now is to try and understand how these folders work so it's important that you understand when you see a folder in parenthesis it's not part of the URL until it reaches a normal folder and then this is reachable at just slash login same is true if I just have root and inside page. TSX right so this is now accessible at just a slash it doesn't matter what the organizational folder is named the only thing that matters it that it is an organizational folder and you can notice that using the parenthesis so I just wanted to cover different types of folders inside of the app folder we covered the uncore folder which we're going to use throughout this project when we want to add components which are not reusable so all reusable components are going to be inside of our components folder where our shat CN button is right now but for example if I only want to create a component like a nvar in the landing page right there's no point in making that reusable so what we're going to do is we're going to create the underscore components folder and put Navar inside of there that way we're going to ensure that that folder is nowhere rendered inside of our routes but we can still use it as a component I hope I made this clear enough if you're confused don't worry we're going to go slowly and I'm going to explain it once again while I'm doing that but for now this is perfect great great job so what I want to do now is create our marketing page or as we're going to refer to it landing page before we do that I quickly want to address that I added a class h- full in this root layout right here and the background color of red but if you look at the browser it seems like this is not taking up the entire space as you would might expect with this H full class right here and that's because we're missing one thing inside of our app folder global. CSS right here we're missing a class name which is going to add height 100 to HTML element to the body element and to the root element so write HTML comma body and comma column root like this and go ahead and write height 100% like that and let's refresh and there we go now you can see that our H full class which we've given inside of this out layout right here is working and it's taking up the entire space so what I want to do now is I want to go ahead and remove everything we don't need in here so we created some folders to explain all the types of folders which we're going to work work with but we can for now remove the out folder entirely we don't need it so you should get a 404 if you go to/ register or/ login so you can just go to plain local Hill 3000 where we have our delete button and instead of calling this whoops instead of calling this a root folder instead I'm going to reame this to marketing right so remember what I said when a folder is in parentheses it doesn't matter what it's called it's not going to be the part of URL so you can safely save this and once you do these changes with the root Pages you're often going to get this weird unsaved change inside of the next folder which is the cache right so you can fix this very easily just click inside of the file and save it just like that if you mess it up you can always completely remove this uh next document so if you think you're having some problems with the cache let me show you how you can fix that so you don't have to do this if everything is working correctly for you but I just want to uh show you a quick fix for it so I'm just going to close this terminal so if you Contour an error with the cache you think you've messed it up no worries you can just do this you can do RM rf. nextt so just remove the next folder or if you're using Windows and this command doesn't work for you you could also just right click on it and delete right uh and then what you're going to do is just mpm runev that's it and take a look now you're going to see that I have a new next folder generated right here so it's that simple to fix that if you think you've messed it up so no worries just remember refresh your local close to once you do this uh great so what I want to do now make sure you rename this to marketing and we're going to leave it like that for now and instead uh let's go inside of our layout. DSX and what I want to do is I want to change this metadata as you can see now my tab has a title of create next app and I want to change that so it actually uh uses the name of the app we are going to create which we're going to call Josan so let's give it a type of Jan like this that's going to be the title let's give it a description well you can do what ever you want but uh for example an app like this would have something like the connected workspace where better comma faster work happens something like that it really doesn't matter great and what I want to do now is I want to show you uh how to add uh how to change this little icon that we have right here also called a favicon as you can see right now it's showing a versel icon here and the reason it's showing that is because you have a favicon.ico in side right here so if you technically remove this icon and refresh your page as you can see now it's gone because it cannot read any favicon but you don't have to always put a favicon.ico inside of the app folder to add a favicon you can also keep it wherever you want for example in the public folder but you do have to tell layout metadata where it's located if it's not inside of the app folder so that's exactly what I want to do now so I want you to go inside of my GitHub where I have my Logos and images right or you can of course find any logo online you want so go inside of my GitHub and go inside of the public folder and you see I have a lot of images here which you are going to use but the ones I want to focus on now are the logo. SVG and Logo Das dark. SVG so I'm going to copy log logo. SVG and I'm going to click download row file like that and I'm going to go ahead and drag and drop that file inside of my public folder so I open the public folder here and through Chrome I'm just dragging dropping it into the public right here and I have a little extension which previews that logo here so it's just an SVG file and go ahead and do the same thing still inside of my public folder with logo D dark so this is a logo which we're going to use if we are in dark mode right so let's go ahead now and let's drag and drop that as well in the public folder here so logo D dark and drag it in the public folder so it's the together with logo.svg perfect now that we have that I'm going to go back inside of my app folder layout right here and I'm going to add icons object and I'm going to write icon to be an array like that object media and we're going to write if in parenthesis refers Das color- scheme to be light so if we are in light mode the URL is going to be slash logo.svg and the HRA it's also going to be/ logo.svg so when things are in the public folder you don't have to write public they are immediately under the slash right so the reason I added two icons is because as you can see in my tab I'm using dark mode on my Chrome and it would be weird if the icon was dark as well right it would barely be visible so I want to use light icon if I am uh on dark mode but a dark icon if I am on light mode so that's we going to add an array of icons here so you can just copy and paste this below and change this one to be prefers color scheme to be dark so if we are on dark mode in that case we're going to use logo D dark. SVG and there we go look at my browser now see how it says Jos and see how I have a nice little icon which is right this one it's using the dark one right now because I'm in dark mode by default but if I if I switch my Chrome to light mode it's going to be using this one so it's going to look better because the tabs are going to be white perfect so we have that solved so I just wanted to do that and what I want to do now is I want to go inside of the marketing and I actually want to start uh creating the marketing page so let's go inside of marketing page. vsx here and I want to go ahead and change this to be constant marketing page like that and I want to export default marketing page so I just change the way I export default it doesn't really matter and let's see the mistake I made marketing oh I missed an page all right so marketing page make sure you don't have any typos and what I'm going to do now is uh modify this so we don't need the button at this moment instead let's add a a div with a class name of Min dh- full Flex flex-all like this so we're creating our outmost div which has a minan height of 100% And we already prepare that this is going to be a flex box the reason we prepare that it's going to be a flex box is because we're also going to have a footer which we want to keep all the times at the bottom and the way we can do that is by using Flex box so when we create our footer element we can just push it all the way to the bottom using Flex perfect so what I want to do now is a div inside with some other class names so let's go ahead and let's write class name to be Flex flex-all items d Center justify Das Center on medium devices it's going to be justify Das start text is going to be centered gap of each item inside of this div is going to be separated by the value of eight which is two Ram or 38 pixels Flex is going to be one so this is what I was talking about so this div is going to take majority of the screen and then below that we're going to add a footer so the footer is always going to be at the bottom because of this Flex one option right here and because the outer div has the flex layout perfect and let's also give it some padding so from both sides padding six and from the bottom padding 10 perfect and inside first thing I want to add is a heading component so if you save you're going to get an error because heading component does not exist yet so let's go ahead and let's see how we're going to do this so let's go inside of the marketing folder and remember what I told you we could technically create the components folder just by writing components but remember that is going to be part of the URL and we don't want that so what we're going to do is create it with underscore components like that and you might be wondering well why not just reuse this components folder outside of the app folder well I want to keep that components folder for reusable components like buttons and dialogues models other stuff we're going to have but whatever I write in this components folder it's only going to be for the marketing or for the landing page so it doesn't make sense to share it with the other files so let's go inside and let's add our heading. DSX component and let's go ahead and let's mark this as use client so this is not going to be a server component it's going to be a client component because we're going to have some actions uh in on our buttons inside great so let's go right let's go ahead and write export const heading like this and let's go ahead and let's return a div with a class name of Max W3 EXL so we limit how wide this uh heading can go and let's give it a space- y-4 so all elements inside are evenly spaced out perfect now let's write an H1 element and we're going to write your ideas documents and plans unified welcome to and then we're going to write a span element and we're going to write Josan and let's give this span element a class name of underline now let's give this well before we do anything let's actually go back to our page. TSX and let's import this heading so we can actually see what we're doing so make sure you import heading from slore components slhe heading like this and go ahead and save this and there we go you can see how I have this text right here in the center now and now let's go ahead back to this H1 element and let's go ahead and let's give it a class name of text Dash 3 Excel on small devices is going to be text- 5 Excel and on medium devices is going to be text- 6 Excel so we're going to have different size of text depending on how wide our screen is and we're also going to give it a font bold on all devices like this so currently we are on mobile mode right but if I expand you can see that it looks much different great and let me just reset my zoom so it's in something uh expected like this there we go so it's uh like that now below the H1 element let's add an H3 element and inside we're going to write Josan is the connected workspace where and let's manually add a little break here better faster work happens like that so you can see we have a much smaller text below this H1 element and let's give this H3 element a class name of text-base let's give it a small device of text- XL let's give it a medium device of txt-2 XL and let's write font Das medium great and now what I want to do uh well we can actually leave it like this for now below this H3 element the only thing we can actually do is add a little button here so we can already prepare so just add a button and import that from button and/ components UI button and we're going to to write enter Jan like that and let's also write a little arrow right icon from Lucid react so you already have lucid react installed when you installed shat CN UI so make sure you import error right from Lucid react and let's go ahead and give it a class name of h-4 W-4 and ml-2 like that great so you can see that now we have a little uh heading here we have the description and we have a button here of course this is later going to become Dynamic depending if we are logged in or not all right so now that we have our heading we can go back inside of the marketing page and below the heading we can now add a new component called Heroes and inside of Heroes we're actually going to add all of our uh nice images so let's go ahead and create the heroes component the same way we did with the heading component so instead of the underscore components create heroes. DSS X and let's go ahead and let's import image from next SL image and let's export con Heroes and we can just do a quick little return function with a div saying Heroes like that go inside of page. vsx and import Heroes the same way you did The Heading and go ahead and save this now and now we have a text here which says Heroes what I want to do now is I want to add the images we need inside of these Heroes so prepare your public folder the same way we did with the logos right and go back inside of my GitHub and go inside of my public folder and the ones we need right now are documents documents DD dark.png and we also need reading and reading D dark.png so first let's do the documents so documents. PNG right here and let me just expand this so I can find find the download button like that and I will drag and drop it inside of my public folder so now I have documents and now let's use the documents Das dark the same way you can see how this one is white so it can be so it can look good against a black uh background and do the same thing with reading so reading. PNG here let's download that as well let's uh pass it inside of the public folder and let's also copy the reading dark.png like that and let's move it inside of our public folder perfect so just make sure that you have the document the do sorry the documents documents dark and that you have the reading dark and reading great you can go back inside of your page now and I'm going to expand my code screen here and I'm going to go back inside of my uh Heroes component which I just created inside of the marketing undor components Heroes right here let's give this div a class name of flex flex-all items D Center justify Das Center Max dw5 Exel and inside of that create another div with a class name of flex items Das Center and inside create a div which is going to hold our image so class name relative W open square brackets 300 pixels h- square brackets 300 pixels as well on small devices the width is going to be 350 pixels and on small height is going to be uh 350 pixels as well on medium devices height is going to be 400 pixels and on medium devices width is going to be 400 pixels as well and inside you can now render image component which we imported above from next slash image and give it a source of Slash documents. PNG give it a property of fill and give it an ALT of documents and let's also give it a class name of object Das contain like that there we go so you can see how we have a nice little uh documents image Here and Now what I'm going to do is I'm going to go outside of this div which is wrapping this image component and create a new div like that and let's go ahead and write a class name here relative h- 400 pixels W Das 400 pixels as well and inside render another image component with a source of SL reading. PNG a fill property and a class name of object D contain so you might be wondering well this looks a bit weird well I only want to show you how this will look because now we're going to go back inside of this let's just see what's missing for the image sorry oh we are missing an ALT property so let's give it an ALT property of reading there we go and now go back inside of this div which is encapsulating the reading image and hide it on mobile devices so hidden by default but when it reaches a medium it's going to be blocked so you can see on mobile we're not going to render it because we don't have that much space to work with but when we expand you can see that it's going to go uh it's going to come back to life here so that's what we want all right uh and now what I want to do is I want to go back inside of page. vsx our marketing page. TSX where we have heading and the heroes and below that let's add a footer component so the same way uh we did with the heading and the heroes and now let's go back inside of the components folder and create a new file footer. PSX and go ahead and Export con footer and return a div saying footer go back inside of page. DSX and render the footer from _ components footer and you can see all the way at the bottom here we have our footer text we can now go back inside of the footer component let's give it a class name of flex items Das Center w- full padding of six let's give it a BG of background and let's give it a z- 50 so we have a z index Here and Now I want to create a logo component so you can see when I save now we're going to get an error because it doesn't exist but we're going to create a logo component which is going to be shared across the footer and across the nav bar which we are yet to create so let's go and inside of this component folder where we have the footer The Heading and the heroes create a new file called logo. DSX go ahead and import image from next slash image and go ahead and import popins from next SL font SLG Google so our logo is going to have a special font and go ahead and import CN from s/ Li utils so you got this when you installed chat CN UI and the CN library is going to be used to dynamically append classes to our our Tailwind uh elements uh without the fear of overriding or incorrect merging so yes you could technically do it with template literals but using this library is a better and the proper way of doing it you're going to see what I'm talking about in a second but before we get there let's actually Define this Poppins font so cons font is equal Poppins open an object subsets open an array and write Latin and give it a weight or 400 and 600 and now export const logo and return a div with a class name of hidden so on mobile devices is going to be hidden on medium it's going to turn into Flex items is going to be Center and gap between the two items is going to be two and now let's add the image component and let's write source to be/ logo. SVG which we already have let's give it a height of 40 let's give it a width of 40 as well and let's give it an ALT of logo like this and what I want to do is go back inside the footer and you can now import that from do/ logo because they are in the same folder and if you now expand your screen you can see little logo here at the bottom on mobile devices it's hidden because we don't have that much room to work with but on large devices it's right here invisible great so let's go back inside of this logo because we're not done here and let's add a paragraphs here saying Jan and let's go ahead and let's give this paragraph a class name and it's actually going to be dynamic class name so go ahead and write CN and let's write font semibold and then let's append this Dynamic class from this font so font. class name and now if you expand your screen you can see how we have a nice little Jan here so if you cannot see this make sure you expand your screen or you zoom out to a specific level so you can see it all right so now that we have this uh what I want to do uh is I want to go back inside of the footer component below the logo here create another dip the class name of MD ml- AO W-4 full justify Das between MD justify Das and flex items D Center Gap dx-2 and text- muted D foreground and inside we're going to render two button elements so import button from at/ components UI Button as I did right here at the top and I'm just going to keep these two separated and in here I'm going to write privacy policy see I'm going to give it a variant of Ghost and the size of small and you can copy and paste this and you can change this one to be terms and conditions and there we go now you have two little uh items here of course we don't actually have privacy policy in terms and conditions but if you want to use this for your real website I just prepared this for you here and you can see that when you collapse they are each on its own side and we don't have the logo but when we are on desktop mode they are all the way to the right here perfect so now that we have this done we are finally ready to create our navbar so let's go and create a layout for our marketing page so I'm going to go inside of the app folder inside of this marketing folder right here let me just expand my screen a bit so I can resize the Navar here and besides the components and page. DSX I'm going to create a new file called layout. DSX because remember each organizational folder can have its own layout and in this case our marketing layout is going to have a nov bar so let's go ahead and let's write marketing layout let's extract the children let's give this children a type of react. react node and let's go ahead and let's return a div the class name of h- full and let's go ahead and return a main element where we're going to render the children and in the main element add a class name of h- full and padding top of 40 and above that add a navbar component which if you save right now is going to give you an error so let's go inside of our components folder and create a new file nvb bar. DSX and let's go ahead and Mark this as use client and let's export cont Novar and let's return a div saying Novar and let's go back inside of layout. DSX and let's import it from do/ components Novar like this so we no longer have that error great now we can go back instead of navb bar. DSX and what I want to do is I want to create a hook called use scroll top so I want our navb bar to not have any border while we are not on the top but if we are zoomed in like this and if we scroll down I want the Novar to follow that and add a little border to it you're going to see in a second what I'm talking about so let's go ahead and let's create uh that hook so we are going to create a new folder called hooks at the root of our application so you can just click on a random file like this just make sure it's not a folder click on a random file outside of any folder here and then you can create a new folder at the root called hooks like this go inside and create a new file called use- scroll dt. DSX and let's go ahead and let's import use State and use effect from react let's export con use scroll top let's give it a uh threshold to be a default value of 10 let's add a state scrolled set scrolled so we're going to detect whether the user scrolled or not and use state by default is going to be false and now let's add a use effect which is going to have a listener here so we're going to write uh window. addevent listener on scroll and we're going to pass in a handle scroll function which we don't have now but we're going to have in a moment and we also have to write a return function which is unmount where we remove the event listener so window. remove event listener scroll and the same one handle scroll which we don't have yet and then go inside of use effect and write const handle scroll like that and write if window. scroll y position is larger than threshold we're going to assume that user has scrolled so we're going to set the state of scroll to True otherwise if user goes back to the top we're going to set set the state of scroll to false so this is going to be just a little trick so we have better user experience and let's also pass in the threshold prop in the dependency array and let's go ahead and let's return scrolled so this state value right here great and now we can go back inside of our app marketing components nvb bar. DSX and let's go ahead and let's add that scrolled so scrolled is going to be use scroll top from at/ hooks use scroll top and now in this div we're going to add a class name which is going to be dynamic using the CN library from s/ li/ utils and let's go ahead and first let's give it some default classes which are not going to be dynamic so Z of Z index of 50 BG background fixed top zero plus FX items D Center w- full and padding of six great and then inside sorry uh then after this default classes add a comma and if we have scrolled away from the top only then are we're going to add a border bottom and a shadow like this so only when we scroll we're going to have a little uh border so let's see if we can already do that when I scroll there we go you can see how I have nice little Shadow here but when I'm up to the top you cannot even distinct that this is a separate component from the background but when you scroll see how you have a nice little effect here so it's details like this that make the landing page better great so now that we have that I'm going to replace this navbar with a logo component from do/ logo which we created like that perfect uh and let's just see uh okay so it's right here here perfect great and now besides the logos I'm just going to zoom this out so I'm in desktop mode make sure you are in desktop mode as well so you can see your logos and beside the logo here I'm going to go below it and add a new div with a class name of MD ml- AO MD justify Das and justify Das between on mobile devices of course w- full Flex items D Center and GAP dx-2 and all I'm going to write here for now is going to be um login like this so we don't have the button for that yet but I just want to show you that it's going to be here or on mobile it's going to move to this side perfect so now that we have that uh what I actually want to do is I want to enable light and dark mode for this landing page so in order to add dark mode I'm actually going to go ahead and follow the shat instructions for it so go to shaten website and click on components here and then you can find this tab which says dark mode go ahead and select nextjs and let's now follow the steps which we need to do so first one is to install next themes package so I'm going to copy this for npn because that's what we are using and let's go inside of the terminal here I'm going to open a new terminal and I'm going to paste npm install next- themes and let's go ahead and do that perfect and then what we're going to have to do is create a theme provider so that is step two so go ahead and copy this entire code if you for any reason cannot find it on the shatan website you can always find it in my GitHub so I'm actually going to put it in a different uh space so I'm going to go inside of components and I'm going to create a new folder called providers so I just want to separate the providers from the rest of the components and inside I'm going to create a new file team- provider. DSX and go ahead and paste everything inside so if for any reason you cannot find this on the shatan documentation you can just visit my GitHub and go ahead and find the components providers tee provider and just copy it from there perfect so now that you have that the step three is to wrap the team provider inside of our layout so let's go ahead uh and let's do that so we're going to be doing that inside of the root layout so let's go inside of the app folder layout. DSX so not the marketing layout the app folder layout. DSX where we have our title description and dynamic icons here and let's go ahead and let's just collapse this children inside and let's just wrap this in theme provider and now we can see that there are two Imports you can either import it from next SL themes or from s/ components providers theme provider you have to use this at/ components providers theme provider the one we just created otherwise it's not going to work so go ahead and copy that and save it like this and make sure you have the team Provider from as/ components providers team provider and what we have to do now is we have to give it some attributes so go ahead and give it an attribute of class let's give it a default theme of whatever is the system theme let's give it enable system option as well let's give it disable transition on change prop and storage key is going to be Jan Das theme like this so you can do whatever uh you can write whatever you want this for example you can put- two it doesn't really matter just make sure it's Unique uh and I think that should actually be it one more thing I want to do is go to the HTML and I want to add suppress hydration warning like that because changing the theme can cause some hydration warnings which are not really important to us so we can suppress them with this prop to the HTML tag great so now as you can see when I refresh my dark mode is activated for you that might not be the case so don't worry if it's not I use dark mode on my system by default and since we added the default them to be system and enabled the system and I added a storage key that used my system theme so don't worry if yours is not working at the moment because now we're going to add a button in the nav bar so that user can click and change the theme that they want so we have to do that by going back inside the shats in UI we just did the step three right here and now we have to do the step four add a mod toggle so let's go ahead and let's click on code so this is what it's going to look like right and now let's go ahead and go inside of code here and just copy this entire thing and go inside of your components folder right here and create a new file mode Das toogle do vsx so let me show you exactly so I just created it inside of the components folder so not inside of UI but inside of mode that toggle and just paste the entire thing inside and as you can see now we have an error here because we cannot find this drop-down menu that means we have to install the drop down menu from shat CM so let's quickly go back inside of our terminal here and let's go ahead uh and I'm just going to uh zoom out actually I'm not going to zoom out I'm going to close this terminal and I will shut this down and go ahead and uh write the following npx shat cnsi at latest at dropdown-menu like this so we have to install that package in order to have our mod toggle working so now it's installing the drop down menu let's give it a couple of seconds and once it's done you can see that you're no longer going to have the error inside of the mod toggle so I'm just going to run my app again and there we go you can see that now there are no errors here because this exists so so if I go inside you can see that I have all the drop down code here but this is what we are focusing on The Mod toggle make sure you save this file make sure it's named mod toggle make sure ins it is inside of the components folder and now what you can do is go back inside of marketing components nvb bar. vsx right here and instead of uh this login text add a mod toggle from add/ components mod Das toggle like this perfect and I'm just going to move it to the top with the others great and now if you go back inside of your application and refresh instead of this there we go I have a little Moon icon and I can change this to light mode now or I can change it to system so now we have enabled light and dark mode but we're obviously missing something when we switch to dark mode we cannot see our pictures and some things just seems weird right so let's go ahead and let's resolve that now first thing I want to resolve this is this logo right here in the navbar and in the footer so we have to find that component let's go and close everything and let's go inside of the app folder marketing components logo. DSX right here and what I'm going to do is I'm going to add give this image a class name of dark hidden so on dark mode the logo is going to be hidden and then I'm going to copy and paste this just below like this and I'm going to change this source to be logo Das dark but this one is going to have a different class name it's going to be hidden by default but if it's on dark mode it's going to be block like this and let's take a look at it now and there we go you can see how now on dark mode we have the opposite white logo right here both in the footer and in the nav bar here great but now let's just go ahead and quickly revisit our nav bar here and I want to give it a different background if we are on dark mode so let's go ahead and let's give it a dark property so if we are on dark mode let's give it a background of open square brackets and write a hex code of uh whoops just a second my apologies 1 f 1 f 1 F like this and there we go you can see that now our Navar has a slightly different color then the rest of the page so we're going to reuse this you can actually copy it like this and now we're going to add it to some other stuff that we need like our layout so let's go inside of our layout but only the layout for the marketing so go inside of this layout we have the h- full right here and go ahead and also paste the dark background to be 1 f 1 F 1f there we go now you can see that the nov bar matches the background of the layout and now we have to do that same thing but for our footer so inside of components go inside of the footer here and just add this to the end and there we go now our footer matches the color perfect and you can already see how the images are becoming a bit more clearer but that's not what we want to do what we want to do is we want to use different images depending on the light and dark mode the same way we did with our logos so what we have to do is we have to go inside of our heroes component so go inside of underscore components heroes. DSX right here and let's find this first documents. PNG and besides the object Das contain class I'm also going to give it a dark whoops dark hidden class like that and then I'm going to go ahead and copy this below and I'm going to change this one to be hidden by default but on dark mode it's going to be block and instead I'm going to use document dark.png and now as you can see this color matches the is more visible in dark mode now and if I switch to light mode you can see how it reverts back to the dark one so let's continue working in dark mode and let's fix the issue for this icon right here so below that we have the reading. PNG let's go ahead and let's copy and paste this let's change this to be reading do dark.png let's give this first one a class name of dark hidden and let's give this one Default hidden and dark to be block and now if you go ahead and expand your screen there we go you can see how better this looks now and if you switch back to light mode there we go all of it looks beautiful as well great great job so now that our landing page is almost finished what I want to do is connect it to our back and then our database and I also want to add authentication in order to achieve those two things we're going to be using convex as our backend and as our realtime database and we're going to be using Clerk and connect it with convex for our authentication so what I want you to do first is create an account with conx so go ahead and find the login button right here and just click log in with GitHub like this so make sure you create an account and then you're going to be redirected to the dashboard as you can see right now I already have a project here which I've used to develop this application but what we're actually going to do is we're going to create a new project so you can try and click here but you're just going to get a popup to follow how to create a project in one of the quick starts right here so you can either click on link like this or if you having trouble finding this don't worry so I'm going to show you how to do it from their website so you can just go ahead and go on developer go on documentation right here and find the quick starts right here and once you're in the quick starts go ahead and select nextjs as your language so we already have created our app so no need to do this step what we have to do is npm install convex so let's do that first I'm going to go inside of my terminal here and let me just go ahead and clear everything and I'm going to go ahead and run mpm install convex like this and after convex has been installed in our Pro project we're going to run a command npx convex Dev and as you can see this will prompt you to log in with GitHub and also to create a project and it's also going to add some stuff in our environment variable so we don't have to do that ourselves so copy this command npx convex Dev and go ahead and paste it here so npx convex Dev and for you it might be a little bit different I'm not sure if you're going to get this question immediately perhaps you're first going to need to log in using GitHub so if you see any links inside of your terminal just click on it and log in with GitHub if not if you're seeing this what I'm seeing just go ahead and choose new project so this pre-selected option now it's asking us for a name and you can see that it already prefilled the name with the name of my folder so it's notion clone correct and now just press enter and there we go it created an entire project for us and now you can see that you can just follow this link right here so just click it and open it and it's going to open that dashboard which you just saw a moment ago so now if you take a look in your team you're going to have one project so this is my old project and this is the new one which I just created and as you can see from this dashboard you're going to be able to see all of your data all of your files your functions your logs your history everything and your schema once we create it so it's going to be very useful to follow this dashboard along what I want to show you now is what this npx Con conx Dev command so this which we just run right here which created our project you actually don't have to stop this terminal right uh in order to use convex you're going to have to keep this running alongside your uh mpm runev but we're going to get to that later so for now just keep this running in your terminal and let's take a look at everything new we have now as you can see we have a new convex folder and we have environment. loal so inside of this environment. local as you can see we have the convex deployment ID right here and we have the next public convex URL and we also have a convex folder inside and inside of it we have some configs we have some generated stuff but we're not going to worry about that too much what you need to know that inside of this convex we're going to create our API functions our queries and our mutations perfect so now that we have this set up what I actually want to do is go back uh inside of the convex page go inside of the documentation here and if you want to you can explore for yourself how to set up a clerk authentication but don't worry we're going to do it together so what I want you to do is go to clerk.com or use the link in the description and just go ahead and log in after you're here just go ahead and create a new application in my case this is going to be notion Das tututorial like this and you can play around with how you want your users to sign in in my case I'm going to disable the email I'm going to disable Google and instead I'm just going to select GitHub if you want to you can do it ex exactly like that or of course you can select whatever you want and just click create an application like this and now that you have these two items right here go ahead and immediately copy them and while we are in this environment file let's also paste them just below this next Publix convex URL so make sure you have the next Publix uh next public clerk publishable key and clerk secret key like this um great what we have to do next is go back and just revisit the convex documentation so where am I right here so convex with clerk so we signed up for clerk we created an application in Clerk and now we have to create a jvt template so let's go ahead and do that so go inside of clerk right here and as you can see here in the sidebar I have jvt templates like this and let's go ahead and let's create a new template and select convex right here because that's what we're using uh leave it a name to be convex like this and you can uh let's just see if we have to modify anything I think we can just leave it right here so in the jbd SE okay no not just new template and choose convex and we have to copy the ISS URL all right so let's go ahead and do that let's go inside of our Clerk and we have the issuer right here and just go ahead and copy this so find the issuer input and click copy and then click apply changes like this and there we go now as you can see you have a new convex jbt template right here and let's see what we have to do next so we have to create out. config.js inside of our convex file so let's go ahead inside of our convex folder and create a new file out. config.js and let's go ahead and let's export default providers like this domain and in the domain let's paste our issuer URL which we just copied from the clerk JT template and our application ID is going to be convex so just make sure you don't misspell any of this stuff if you want to you can copy it from here as well so it needs to be providers which is an array which has an object the domain is our clerk issuer which we copied from here right and our application ID is the name of uh is the name of our jvt template which we left to be conx so it needs to match application ID make sure both I and D are capital like this and you can just save out. config.js so now that we have this out. config.js just make sure that inside of your terminal you have this running right so as you can see for a second I had an invalid out convi I think that's because I misspelled application ID but after that it looks like everything is okay so if you want to you can shut down the app and run npx convex Dev again so after you've created this out. config.js file just run npx convex Dev again and confirm that there are no errors in this function there we go everything seems okay and that means it successfully connected to convex no errors perfect now we can close clerk we can close this dashboard and let's go ahead and see what we have to do now so as you can see we deployed our changes right now by using this we confirm that there are no errors and now we have to install clerk so let's go ahead and open a new terminal now so make sure you have the npx convex running and now let's go ahead and let's do npm install at clerk SL clerk Das react like this and go ahead and press enter and once that is finished we're going to go ahead and we're going to create our convex provider and we're going to combine it with clerk provider so let's go ahead and let's close this just make sure you have npx convex Dev running in the other terminal go inside of the components folder providers and create a new file called convex provider. DSX so let me just expand this so you can see convex D provider . DSX mark this as use client like that go ahead and import react node from react go ahead and import convex react client from convex SL react go ahead and import convex provider with clerk from convex SL react Das clerk go ahead and import clerk provider and use out from at clerk SL clerk react like that now Define the convex constant so const convex is going to be new convex react client and inside passing process. environment. nextore public convex URL so if you check your environment. loal you have that right here nextore undor convex URL you can copy it from here and paste it it again here to confirm that you didn't do any misspellings and we have a little error here so you can just put an exclamation point at the end for it to go away great and now let's export const convex client provider like that let's go ahead uh and whoops let's go ahead and let's extract the children from here so the children are a type of react node and let's go ahead and let's return a clerk provider and let's give it a prop publishable key to be process. environment. nextext corpu clerk unpublishable uncore key and the same thing you can go ahead and inside of your environment. local and just confirm that you have that here nextore public undor clerk publishable key you can copy it from here and paste pasted here to confirm and just put an exclamation point at the end so there are no errors and if you don't have this well you should just get it from clerk.com so go inside of clerk.com go inside of your dashboard go ahead and select the application which we just created and there we go next public clerk publishable key so just make sure you have that in your environment variables great and now that you have that let's go back inside of the convex provider and let's go ahead and let's give it a children of convex provider with clerk like this let's give it a prop use out to be use out which we imported right here from clerk react let's give it a client of convex which is this constant which we defined right here and then inside of that we can just render the children and now our entire application is going to use authentication using convex as a database and clerk as the out provider and now what we have to do is go inside of app folder layout. TSX right here and just above the theme provider you're going to go ahead and import that convex client Provider from at/ components provider convex provider and just wrap the entire team provider inside of it like this so make sure you have this imported and there we go so now uh if you refresh you should not see anything because we have never started the app so inside of my terminal I have one terminal which is running npx convex Dev and in my another terminal I'm going to run mpm run Dev so I can finally see my application so let's just refresh this to confirm that we don't have any errors and there we go seems like we don't have any errors and what we can do now is we can finally use some hooks to check whether we are authenticated or not and we can also fire models to actually log in using GitHub in my case or whatever you selected in your case so let's go ahead and do that now so first place I want to add authentication check to is the nov bar right here so let's go ahead and let's find our app folder marketing uncore components navb bar. TSX right here and besides using the use scroll top hook we're also going to be using const use convex out like this and you can import use convex out from use convex out from convex SL react like this and I'm just going to separate this import great so now we have used convex out here and from here we can extract is authenticated and is loading so now what I want to do is I want to go outside Above This mod toggle right here and I'm going to add if is loading in that case I'm just going to go ahead and render a paragraph saying loading like this and below that I'm going to go ahead and I'm going to write if we are not authenticated by adding the exclamation point at the end of the Boolean and if we're not loading in that case I'm going to render a fragment like this and I'm going to render sign in button which you can import from CL uh from clerk react so go ahead to the top and let's go ahead and let's import sign in button from add clerk clerk react so we have the sign in button now like this and inside of it we're going to render our button from s/ components UI button so make sure you added the button from m/ component UI button I'm just going to move it here all right and inside of here I'm going to write log in like this and I'm also going to give it a variant of Ghost and the size of small and I'm going to give this signin button a mod of model like that perfect and then below that I'm going to copy uh and paste this so inside of this fragment I just copied and paste this entire signing button and we're going to do the very same thing but this one is going to say get Jan three like that and it's not going to have a variant ghost like this and now if you click on this you should have as you can see a GitHub uh login model open perfect so we have that working now and as you can see this is how it looks like on mobile perfect so what I want to do now is I want to modify this loading a little bit so if you you refresh you can see how I have a loading text here for a couple of seconds instead of having that loading I actually want to have a little spinner here so let's go ahead inside of our Global components folder and let's create a new file spinner. DSX like this and I want to import loader from Lucid react and I want to import CVA and type variant props from class variance Authority and I also want to import CN library from lib utils so the reason I'm importing the CVA from class variance Authority is because I want to create uh some different variants for this spinner right I'm going to reuse this spinner A lot of times in the project sometimes I'm going to want it to be a little bit bigger sometimes I'm going to want it to be a little bit smaller so the best way to do that is using class variance Authority so let's write const spinner variance to be CVA and let's first give it some default classes so that's going to be text muted foreground and animate Das spin so that's going to be the default classes of this spinner and then we're going to open an object and write Varian like this and we're going to add size as a variant default is going to be h-4 and W-4 below that we're going to add a small option for it so this one is going to be h-2 W-2 below that we're going to create a large option which is going to be h-6 and w-6 and below that we're going to add an icon which is going to be h-10 and w-10 now we're going to Define which one of these is default and well you can already guess by name but we have to manually select that by adding default variance size and it's going to choose default so this is going to be the default size of our spinner whenever we add it so we don't have to uh manually add the spinner variant every single time and now let's create an interface spinner props to be extends variant props like this open this pointy brackets and write type of let me just expand this it's going to be type of spinner variance constant which we have defined right here above great and you can just go ahead and open an empty object at the end and now go ahead whoops where did I go and now go ahead and Export const spinner which will take the size as a prop from spinner props like that and go ahead and return a loader which we imported from Lucid rea act and give it a class name to be CN spinner variance and pass in this size object there we go so we have a nice little modular spinner component here save this file and now we're going to go back here where we currently have the loading and instead we're going to render that spinner from add/ components spinner like this so just make sure you imported the spinner I'm going to move it with the others and now uh if you take a look when I refresh here you can see how I have a nice little spinner here for a second instead of that uh weird instead of that weird um loading text great so what I want to do now is I want to display a different state if we are actually logged in right so let's go outside of this if Clause right here so at the bottom just above mod toggle and let's write if we are authenticated so this time without an exclamation mark if we are authenticated and if we are not loading in that case go ahead and open a fragment again and let's just add a button element inside let's give it a variant of ghost a size of SM and as child prop and pass in a link from next slash link so just make sure you imported next slash link like I did right here I'm just going to move it to the top and go ahead and give this an hre of Slash documents which is a site we don't have yet but we're going to have in the future and just write enter Jan like that and outside of this button we're also going to have user button so we don't have this user button yet so let's go right here where we have the sign in button and let's add user Button as well and one important thing is that in this user button you give it a prop after sign out URL to be a slash and save this document and go ahead and refresh this one more time and now let's try our login here so I'm going to continue with GitHub like this all right and there we go as you can see I'm right here and I have my user button from here and now it says enter Jan perfect so what I want to do now is I want to modify this Hero action uh here as well to also tell me different things depending on whether I'm logged in or not so before we do that I just want to show you that from here you can sign out for example example you can log in back again with GitHub right or you can also go ahead and manage your entire account change your picture log out add different accounts delete your entire account modify some security and stuff like that so for now what I want to do is I want to go back inside of the heading component so heading. CSX inside of marketing folder undor components right here and let's go ahead and let's add our hook use convex app out so we need to import that from convex react import use convex out from convex SL react all right and let's go ahead and let's extract is authenticated and is loading perfect and now let's go ahead and let's wrap this button inside of if we are not authenticated sorry if we are authenticated so remove this exclamation point F if we are not loading only then do you render this button to actually enter Jo right otherwise we're going to go ahead and check for is loading and we're going to render the spinner from at/ components spinner which we created recently so make sure you have that imported and give it a size of large like this and now if you refresh you can see how for a second you have a little uh spinner here and let's go ahead and wrap this inside of a div and let's give it a class name of w- full Flex items D Center and justify Das Center and now if you try again there we go now it's in the center perfect so exactly as we want and now what I want to do is do uh just modify this to actually lead you to uh the documents page because this is a case if we are authenticated so mark this button uh to be as child and inside add a link component from next SL link so make sure you imported that make sure you added link from next SL link as I did right here and give this an HRA of SL documents like this and nothing much should change here except now it should lead to slash documents which if you click it's a 404 page it doesn't exist yet and now let's add if we are not authenticated and if we're not loading in that case go ahead and render the sign in button which you can import from clerk uh clerk react so import sign in button from add clerk clerk react like that give it a mode of model like this and just go ahead and add a little button inside get Jan three and also add an arrow right which you already have imported in here with a class name of h-4 W-4 and ml-2 and there we go so if you try and click on enter J we redirected to a 404 page but if you go ahead and log out as you can see now this button as well opens a GitHub so exactly what we wanted and if we log in from anywhere everything refreshes and as you can see now we have both redirects to currently non-existing page great great job so now that we have our database and our authentication set up let's go ahead and let's create this page which currently yields a 404 so let's just recap what we have to have running for this application so let me remind you of my terminals so in one terminal I have npx convex Dev running which is my back end and in the other one I have mpm run Dev running so make sure you have two of those running refresh your page confirm everything is working great so what we have to do now is we have to create an organizational folder which is going to help our documents page which is currently a 404 so let's go inside of the app folder and the same way we created this marketing route group let's go ahead and let's create a new route Group which we are going to call Main so main application is going to want to go inside and let's go ahead and let's create another folder inside called routes like that and then inside of it create a new folder documents and inside our file page. TSX and then you can go ahead and create documents page with a div this is a protected page so only logged in users should see this page and once you save that you should no longer get a 404 error if you're still getting a 404 error make sure that you are on SL documents and make sure that all of your links uh inside of this Landing screen lead to SL documents so now let's try and log out and let's see if we can manually go to slash documents and it looks like we still can so that's not good if we are logged out I want to get redirected so if the user manually goes here I want to bring them back to the landing page to log in so so in order to do that we can either use the middleware or we can simply create a layout inside of this main folder and let's go ahead and create a layout. vsx inside of the main organizational folder so layout. vsx so it is inside of the main folder not inside of the routes so it's separated like this and now let's go ahead and let's write sfc documents sorry main layout let's extract the children let's give the children a type of react. react node and let's go ahead and let's return a div which is just rendering the children so nothing should change much for now but what I want to do now is add some hooks here so in order to do that we're going to have to map this entire thing as use client and that is okay so usually I would avoid use clients because I want to leverage server components but this time we are working with a real-time database which shines in in client components it is not a mistake or an anti- pattern to work with client components inside next3 next 13 just gives you an option to work with server components if that's what your application needs but since this is a heavily real-time application it makes sense that we use a lot of client components great so now that we marked our layout as use client let's go ahead and let's extract is authenticated authenticated and is loading from use convex out and we can import use convex out from convex SL react like that perfect and now let's check if we are loading we're going to return a div which is going to render our spinner component which we created in the previous parts of the tutorial so make sure you import that and let's give it a size of large and let's give it a class name of h- full Flex items D Center and justify Das Center so it's centered in the middle of the screen so if you try and click on documents now I'm actually going to go manually to SL documents you can see how for a second I have a loading indicator in the middle of my screen great and now we're going to check if we're not authenticated so if we are not authenticated so if not is authenticated in that case we're going to return a redirect from next SL navigation so make sure you add this import redirect from next SL navigation and we're going to lead the user back to our landing page and there we go now that I Sav the file I'm immediately redirected so if I go ahead and manually go to SL documents you can see how it doesn't let me go there I'm immediately redirected back here and since we did this logic inside of the layout file and not inside of a specific page that means that every single route that we create inside of our main application is now officially out protected perfect so that's exactly what we wanted to do here so now what I want to do is I want to style this div a little bit so let's go ahead and give it a class name of h- full flex and let's give it a special background if we are on dark mode so if we are on dark mode the background we're going to use is a specific hex color 1f 1 f one F like that and let's go ahead and let's wrap these children inside of a main element just like that and let's give this main element a class name of flex D1 h- full and overflow-y D aouto great and now let's go ahead and in here render navigation component and if you save well you will not see an error unless you log in so let me just sign in with my GitHub now so I can go to that slash documents page and and there we go now I have an error here great so let's go ahead now and inside uh of this main folder go ahead and create another folder underscore components and inside of it create a new file navigation. vsx let's go ahead and Mark this as use client and let's export con navigation just like that and return a div saying navigation and save the file now go back to your main layout and import that navigation from slore components SL navigation and I'm just going to separate this Imports and there we go we no longer have any errors and you can see how navigation is at this side of the page and now what we're going to do is we're going to slowly create this navigation page so let's go inside uh and let's prepare everything we need so first thing I want to do is I want to wrap this entire thing in a fragment of of its own like this and I want to change this to not be a div instead we're going to use an aside element great and now what I want to do is give this a class name so class name is going to be group group slidebar h- full vg- secondary like this overflow-y Das AO relative Flex W D60 like that is uh and uh sorry w- 60 uh flex-all and Z index is going to be 99999 so five9 like this because we're going to have uh notion style block editor which takes a lot of Z index space so we are going to move it this to the top great so as you can see now we have uh a template for our sidebar right here you can see how it takes this entire space and everything else is moved to this side perfect now let's go ahead and let's fill this side element with some placeholder items so what I want to do now is I want to create a div here and I just want to add a paragraph inside action items like this so we're going to have action items then go ahead below that and create another div and give it a class name of margin top 4 and inside we're going to create a paragraph and we're just going to write documents like this so first we're going to have some action items then we're going to have some documents and then go ahead and create another div right here and this one it's going to be a bit different in fact it's going to be a self closing div like that and give it a class name of opacity Das Z like this and then what we're going to do is we're going to leverage this group SL sidebar so in Tailwind there is a class name called group usually you write it just like this so group right and then what you can do is you can do group- hover opacity D100 that means that this element is going to be hidden in our case it's going to have opacity of zero but when we hover on the entire as side element it's going to change to 100 so why did I write group / sidebar here well I wrote that because that's a way you can differentiate between different groups so inside of this sidebar component we're going to have different groups which we're going to use that trick on but we can do group SL sidebar so we tell it okay this is only supposed to be active on group SL sidebar and if you change group SL sidebar here you also have to change it here but it's not going to be group slidebar like this instead it's going to be group- hover slidebar like this and you can see when I hover over this class it's a completely valid class it's working great so now now that we have that let's continue adding some classes to this element so we're also going to give it a transition class we're going to give it a cursor Das ew- resize an absolute h- full w-1 vg- primary sl10 so we're going to give it some opacity wr-0 and top- z and now if you take a look when I hover over my sidebar I have a little bar here which is going to be used you guessed it to expand our sidebar as needed so let me try and zoom in so you can see you can see when I hover anywhere on the sidebar I have this opened to me so it indicates to the user okay there's something I can do with this and that's exactly uh what we want now I want to go back inside of this as side action right here above this div where we wrote the action items and let's go ahead and add a new div here which is going to render chevron's left icon from Lucid react it's a self-closing tag just make sure you import Chevron left from Lucid react and let's go ahead and let's give this a class name of h-6 and w-6 and let's give this div a roll of button and let's give it a class last name of h-6 w-6 text- muted D foreground like that rounded Das small hover vg- neutral D300 on dark mode hover is going to be vg- neutral D600 absolute top-3 right -2 to opacity Das 0 and we're also going to modify it when we hover so on group Das hover slidebar we're going to change the opacity to 100 and we're going to add a transition class great so you can see that now when I hover we have a nice little icon here but only when we hover so it's not visible it's not visible right now but when I hover you can see that I have this little icon which you guessed it is going to be used to collapse the Entre sidebar as we need it perfect so now that we have this I think we ready to create uh some refs here so let's go ahead and let's go here and let's write const is resizing ref we use ref and give it a false value then const sidebar ref is going to be use ref and it's going to have a type of element ref from react so make sure you import that open another pair of pointy brackets and go ahead and write an element called a side and let's give it a default value of now B below that we're going to have a con Novar ref which is going to be use ref again it's going to be an element ref with a type of div inside and a default value of null great and now let's also just add const is resetting set is resetting to be use state from react and give it a false default value and const is collapsed set is collapsed to be use State false as well great so now that we have that I want to go ahead and install a package called use hooks so let's go inside of our terminal I'm just going to open a new terminal and I'm going to write mpm install use hooks DTS so a very cool package uh if you want to you can also search for a specific hook which we're going to use outside of this package and just create that hook but for a tutorial is just simple for me to install the entire package so import use media query from use hooks DTS and so the reason I'm not using Tailwind break points for this is because it's quite complex to do that especially with the fact that our sidebar is going to be resizable on drag as you saw in the demo right so it's quite hard to do that using just Tailwind so we're going to have to use some tricks with refs and other stuff and we're going to need to use media query to manually Define in JavaScript what's considered mobile and what's considered desktop using breakpoints so I'm going to go above all of this and I'm going to write const is mobile to be use media query and I'm going to go ahead and open a string and write Max Das width is going to be 7 68 pixels so this is the same breako for medium inside of tailwind and then we can change this is collapsed use State default value to be is mobile so if we are on mobile our sidebar is going to be automatically collapsed but if we are on desktop we're going to have an option to collapse it once we click on this item right here and while we are here let's also go ahead and let's add uh params here so const sorry path name it's going to be used path name from next l navigation because if we are on mobile I also want to manually collapse the sidebar every time we click on a different uh item right I don't want our sidebar to stay open because it's going to take the full screen so I want when user clicks on a specific document to collapse the entire sidebar and that's why we're going to use this Pat name inside of a use effect so we know when that happens so now that we have that we're ready to start writing some functions here so let's go ahead and let's first um well let's define uh some functions actually but before we actually can Define any functions we have to assign these refs which we just created I completely forgot about them my apologies so let's go inside the asside element right here and let's give it a ref of sidebar ref like that great and now that we have this as well let's change this into curly brackets like this and let's add a CN library from at/ lib util so just make sure you import CN from lib utils I'm just going to separate this import and once you have that CN right here go ahead and wrap this entire class name inside of parenthesis like that and you can press enter like whoops and you can just press enter like this so it's in the first line and then in the second line so add a comma after this entire string ends and let's add some conditional classes here so if is setting resetting which we defined as a a constant here in that case we're going to add a transition to it so transition Das all e- in- out duration D300 and let's also add if is Mobile in that case width is zero by default and don't forget to add a comma here and as you can see now I don't I no longer have uh my sidebar here I refresh that's because the is uh is mobile right here is true and we wrote a class name here if we are on mobile the default width of the sidebar is zero but only you can see even when I expand only then it's visible right perfect so what I recommend you do is just zoom out so you can actually see the sidebar that you're working with great so we just assigned the sidebar ref here now let's go ahead to this button here and let's go ahead and do the same thing with the classes so I'm going to go ahead and open this wrap this in curly brackets at the CN Library wrap the entire thing inside of CN like this and let's go ahead and add conditional one if is Mobile in that case opacity is going to be 100 by default right so if we are on mobile we're not going to do this trick that only when we hover on something does a button appear That's okay for desktop but on mobile we want it to always be visible so user know knows that they can collapse the sidebar because there is no well there probably is some kind of hover on mobile but it's not the same user experience as on desktop so if it's mobile we're not going to do this whole on Hover change the opacity it's just going to be visible all the time great and now that we have this let's go ahead uh all the way to the bottom here outside of this aide element and let's go ahead and let's create a div here where we're going to have a ref of navbar ref and we're going to have a class name to be CN default classes are going to be absolute top- z z index of 1 2 3 4 5 so five nines like this left is going to be 60 and width is going to be a calculation of 100% meus 240 pixels because that's the value of this left 60 right here so 240 pixels great and then we're going to add some conditional classes so if is resetting in that case we're going to add a little transition here so transition D all is- in- out and duration -300 and we're also and just make sure you don't misspell resetting and we're also going to have another condition if is Mobile in that case left is going to be zero and W is going to be full so what we did here is is our uh position for the Novar which is going to be right here where it currently says this is a protected in documents page so this knv bar is going to have to follow the width and the collaps m and everything that the sidebar does it's going to have to be in sync with the nov bar so that's why we need this a bit of a complex logic here to watch for all of those actions to happen great so now that we have that uh let's go ahead inside and let's add uh a little nav element here like this and let's just write if is collapsed in that case add a menu icon from Lucid react so make sure you import menu icon from Lucid react and go ahead and give it a class name of h-6 w-6 and text- muted D foreground let's also give it a roll off button like this and you can see that now when I zoom in I should have there we go so you can see it's it's overlapping the text currently so it doesn't make much sense but as you can see right here there is a little icon here you can see how it's over my text now let's go ahead to this no element and let's give it a class name of B- transparent bx-3 like this py D2 and w- full like that great so that's enough for us uh for now and now what I want to do is I can finally go ahead and create some actions so let's go ahead and let's find uh this div right here so this is the div which appears when we hover on our uh sidebar this little uh bold line right here that is this div so let's go ahead uh and let's modify it a little bit by giving it an on on Mouse down option let's just put that as an empty function and on click option which is also going to be an empty function so first res resolve this uh on Mouse down option right here so let's go ahead and let's create that function here const handle Mouse down is going to accept an event which is a type of react. mouseevent HTML div element and mouse event like that and it's an arrow function and the first thing we're going to do is we're going to prevent default and then we're going to stop propagation from this event and then we're going to set is resizing ref. current to be true and we're going to add an event listener so document. add event listener whoops listener all right and event listener is going to be Mouse move and it's going to uh it's going to trigger handle Mouse move like this and go ahead and copy the ad event listener and let's just not misspell this so add event listener okay I made a typo here so ad event listener like this and this one is going to be not Mouse move but Mouse up and the function it's going to call is handle Mouse up like this so now obviously we're going to get some errors because none of this exists right now but let's just go ahead and assign this handle Mouse down to this on Mouse down uh empty Arrow function which we created right now and let me just see if I refresh if I immediately get an error I don't but I think that when I try and move this uh looks like I'm not getting any errors but I think I am getting them in the console nevertheless let's go ahead and let's create this handle Mouse move and handle Mouse up so handle Mouse move is going to be used to prise our uh sidebar but when we leave the mouse when we put a finger above our cursor that means we are done with resizing and we can stop resizing right so first let's do the handle Mouse move so let's go below that and write con handle Mouse move is going to accept an event to be a mouse event like that and first thing you're going to check if we have uh there is resizing ref uh inactive so if there is no is in ref do current in that case we can just break the entire function no need to go further and then let's assign the new width to the client x value of this event so let new width is going to be event. client X like that actually it's not let's just rename this to event like that there we go and now let's add some limits to how how much the user can resize the the uh the sidebar to and how much they can collapse the sidebar so if new width is less than 240 in that case new width is going to be 240 so we canot go lower than that and then in the same way we're going to limit how wide it can go if new width is larger than 480 then new width is simply going to be 480 we're not going to let them go above that value and now that we have that let's check if we have both the sidebar ref and the navbar ref so if we have sidebar ref. current and if we have navbar ref. current because we need to animate both of them now in that case let's give the sidebar ref. current. style. width open back TI add an object inside new width with pixels like this and then let's also add the navbar ref. current. style do set property like that we're going to set the property left to be moved let's just open backs again to new with PX like that so let me just expand this a little bit there we go okay uh one more time there we go so we uh moved the naar left property but by the same amount and now we also have to move its width so nabar ref. current. style do set property with is going to be open btic again calculate Open brackets 100% minus open the special object new width and add pixels inside of these brackets right here so let me try if I can expand even more so you can see this in one line so this is how it's supposed to look like navbar ref. current style set property width open back tick and calculate 100% minus the new width to which we just expanded pixels and then end the bracket and end the backtick Great so now that we have all of those we are ready to create this handle Mouse up function you can see we no longer have an error for this one but we have the handle Mouse up error so let's go below this and let's create a constant handle Mouse up which all is going to do it's not even going to have to take any events all it's going to do is set the is resizing graph do current to be false and document. remove event listener Mouse move from handle Mouse move and document. remove event listener Mouse up from handle Mouse up like that then if we did everything correctly I think let's just confirm so there are no errors perfect if we did this correctly I think we should be able to move our sidebar now so let me try and expand this now and there we go you can see how I have a limit to how much I can expand and collapse my sidebar and I can leave it like this and you can see how it's no longer following my mouse but if I hold on this it's following my mouse to a specific break point which we've given it and because of all the flex properties we've given inside of our layout you can see how the entire content is moving with it as well and you can see it right now but in the future we're also going to have a enough bar here which is just as well going to follow the transition of this sidebar perfect so now what we have to do is we have to create this functionality that collapses the entire navigation bar and we also have to create uh this that when you click here it actually opens the sidebar great so first thing I want to do is this button right here so let's create the functions to handle that so I'm just going to expand my entire screen here because we're going to focus on the code now and let's go below this handle Mouse up and let's create a constant which is going to be reset width so reset width is going to reset the sidebar width to its original width so it's not going to collapse it it's going to be used if we expand too much and the user wishes to go back to the original position without dragging they can just click on that line they use to drag and then it's going to be reset to 240 pixels so let's go ahead and first let's check if we have the sidebar ref. current and if we have the navbar ref. current if we have both of those we can continue so set is collapsed is now going to be false set is resetting is going to be false as well my apologies is going to be true because we want to animate uh the uh the uh the resetting of the width you're going to see that in a second and now let's write sidebar ref do current. style. width and first let's check if is mobile we're going to reset it to a 100% otherwise to 240 pixels because on mobile remember we take the entire screen with a sidebar and now let's write navbar ref. current. style. set property so with Navar we work with the property width and with property left so first let's assign the width property and let me just do this in two line so you don't have to do it in two lines I just want you to be able to read what I'm writing so the property is going to check if is Mobile in that case it's going to be zero otherwise it's going to be a calculation of 100% minus 240 pixels like this and below that let's do the same same thing but for the left property so nav ref. current. style do set property the property we're going to modify is left and the value is also going to be dependent on the is mobile uh Boolean so if it's mobile 100% otherwise 240 pixels great so now we have that and now let's just write a set time out here which is going to set is resetting to f so you might be confused by this action why are we doing this why this 300 milliseconds here what is this set setting even used for well if you take a look at our code when the isra setting is active we add a little transition to it with a duration of 300 so that's why we use the timeout to be 300 as well we use it here on the sidebar itself which is the aside element and we also use it for the Novar duration 300 and transition when we are resetting so we are using that because this is not going to be a drag and hold like when we are expanding this is going to be when we click just reset the entire thing so we want to show just a little nice animation for it and we have to use this timeout trick for it great uh and if you're wondering well why not just um where is it why not just use this in the main classes why do we even need it in a Boolean well that's because if you add transition all uh and if you add this class to the main classes here it's also going to be visible when you drag and resize and it just looks very off it looks weird and there's no way to limit it to a specific action in Tailwind besides using uh a combination of JavaScript with it as well uh great so now that we have uh the reset width option let's go ahead and let's assign it to this div there we go we have this on click which is an empty Arrow function and let's now give it the reset width option like this and now let me expand my screen and let me just refresh and now we can see when I expand this and then when I click on this oh looks like it's not is it working or not let's just see there we goes It's Working uh as you can see when you expand just go ahead and click on this element and as you can see how it nicely creates a little animation here to collapse back to its original position and it also moves the entire content with it as well in a nice anim way perfect so that's what we wanted to achieve with that uh reset width function but now we have to create the collapse function which is going to be similar to this reset width function so let's go ahead and let's write const collapse let's check if we have the sidebar ref. current and if we have nbar ref. current and let's set is collapsed to true and set is resetting to true as well and then let's do sidebar ref. current. style. width is simply going to be zero like that and Navar do current. set property sorry do style. set property the property withth is simply going to be 100% And nvar f. current. style. set property left is simply going to be zero and set timeout at the end where we just change the set is resetting to false after a 300 millisecond delay and now we're going to assign this collapse function uh to the onclick of this button right here where is it there we go this roll button right here which we've given let's give it an on click off uh collapse like this so it's this div just inside of the aside which holds the chevron's left icon and now let's try that out so I'm going to refresh again I'm going to zoom out just a bit and you can see when I click here you can see how it entirely collapses and when we click on this one well I think we can already reuse the reset width on that one and that's going to work let me just confirm uh if that is the case uh so let's go ahead now yes we can just do that so let's go all the way to the bottom and find this menu icon where we have the roll button and let's give it an on click of reset withd like this and let's try this out now so I'm going to refresh this I'm going to try and collapse this I'm going to go ahead and click this and there we go you can see how I can now entirely collapse my sidebar and I can expand it perfect so now what I want to do is I want to write some use effects which are going to detect whether we are on mobile or not as you can see now I switched to mobile mode but I can still very much Vis it uh and you can see how yeah on mobile when I expand it takes up the whole screen but still uh what I want to do now is you can see when I expand to desktop it doesn't really work well right when I when I have a sidebar now and if I switch to mobile mode I expect this sidebar to disappear right to be hidden well we can do that using use effects because we have all the necessary functions here so let's go to the top of everything here and let's write use effect from react so just make sure you imported use effect from react where we have the ref and the state and for the first one we're going to check if is mobile so if we suddenly turn to mobile collapse the entire sidebar otherwise we're going to use reset width like this and the only thing we're going to add inside is is mobile like this uh you don't have to worry about the warning for set with because we know that it's going to work we know we are working with refs here which are not reactive in a way that use effect expects it so you don't have to add reset width inside this is going to work just fine and below that let's go ahead and add another use effect which is going to work on the path name change so let's go ahead and add if is mobile here collapse again and Trigger this on path name and is mobile like this uh and we don't have the path name so let's go ahead and add it uh path name perhaps I misspelled it yes path name great uh and there we go no warnings here now and as you can see now when I expand my screen I have a sidebar when I Collapse I don't have a sidebar when I click on mobile it takes up the whole screen but if I go ahead and expand and try then it doesn't it does just what we want it to do if we try from here it works exactly the same perfect so we have entirely finished our animations of the sidebar and now we are finally ready to start giving it some items inside uh great great job all right so now that we have our navigation working what I want to create is this uh this is a protected documents page so currently that's the only thing it says when I'm in this slash documents route so now I want to create a little picture here and a little button which is telling the user that they can now create their first note so in order to do that first thing I want to do is prepare the images we're going to need for that so I'm going to go ahead and prepare my public folder like I did for the hero images for the landing page and let's go ahead and go inside of my GitHub right here and just go ahead and find the public folder which I have and go ahead and download empty and empty dark so I'm going to go ahead and download this one and I'm going to go ahead and drag and drop it inside of the public folder and then I'm going to go ahead and download empty dark download that as well and drag and drop it inside of the public folder great so let's see if I have to rename them there we go so it looks like I have to rename this empty it shouldn't have any numbers in it so I'm just going to rename it to empty.png so make sure you have empty dark and empty.png like this great so now that we have that let's head back inside of our app folder main folder routes documents page. vsx so here where we say this is a protected document page and what I want to do is add a class name to this div with h- full Flex flex-all item D Center justify Das Center and space- y-4 so we now Center this text right here and let's go ahead and let's mark this entire thing as used client because we're going to have some hooks inside and let's remove this text from here and instead let's render an image element from next SL image let's go ahead and give it a source of/ .png let's give it a height of 300 a width of 300 as well and ALT of empty and now now let's give it a class name of dark hidden like that and now let's copy and paste this just above just below this one and let's change this one to be empty Das dark and let's give this one to be uh hidden on default but when we switch to dark mode we are in Block perfect so you can't really see that now because we don't have the functionality to switch light and dark mode inside of this main page but as you can see we have an empty little uh image right here perfect and now below that uh I want to render welcome to uh your name's potion uh sorry your name's Jan that's what we're going to call the app right so let's go ahead and let's import use user from clerk react so import use user from at clerk clerk react like that and let's go ahead and let's extract the user from use user like this and now just below this image right here what we're going to do is we're going to add an H2 element where we're going to say welcome to and go ahead and write user question mark. first name and then add a special character apples like this so this is a way to escape the actual uh this is the sign we're trying to write but as you can see I have an error that it can be escaped so that's why I'm writing apples like this uh so welcome to your names Joan like that now it says welcome to Antonio's Joan in my case and now let's go ahead and give this heading element a class name of text- large and font Das medium great and below that let's add a button Element Make sure you import that from as/ components UI button and inside of that element go ahead and add a plus Circle icon from Lucid react so again make sure you have uh that imported let's give this plus Circle a class name of h-4 W-4 and margin right of two and let's write create a note text like this perfect so we have now finished our empty screen right here and it looks much better than what it previously was what I want to work on now is the back in the navigation bar right here so I'm just going to expand my screen a bit because I want to work in desktop sub mode for now and we're going to go ahead and replace these action items with our actual first item which we're going to use to sign out and to see the currently logged in user of this uh Jan right so let's go ahead and let's go back inside of our app folder main components navigation. vsx so here where we wrote all of that logic on how the navigation bar should behave and now let's go the way down and let's find this first div where we wrote a paragraph with action items and now let's remove that and instead let's render the user item component like this if you save of course we're going to get an error because user item is not defined so inside of that underscore components folder create a user- item. DSX file and let's go ahead and let's mark this is use client like that and let's export const user item user item and just return a simple div user item so now we can go back inside of navigation and import that user item from do/ user item because they are in the same folder like this and there we go we no longer have an error and instead we have a little text here saying user item so let's go back inside of this user item and before we continue let's go ahead and install a couple of shats and packages which we're going to need to develop this to the end so I'm going to go ahead and write npx shat CN UI at latest add Avatar so that's the first one we're going to add so go ahead and click y if you get this if not you're just going to get it install and besides the Avatar we also need a drop-down menu so let's just wait a second for this to install and after this is installed we run a similar command npx shat cn- at latest add drop drop down Dash menu like this so just go ahead uh looks like it says the drop down menu already exists oh so we already installed that I believe when we created the mod toggle button right so you can just uh press no on this one it doesn't really matter uh Great Looks like we already have this my apologies if you don't have it by any chance feel free to add it using this command so let me just confirm inside of the code that I actually have the drop down menu so go inside of your components folder instead of the UI and there we go it's right here drop down Dash menu perfect so we can now uh work with that but I think we have a problem here and the problem is that inside of our navigation if you remember we added a z index of a lot right and let's look at our drop down menu here I think that in here our Z index is much lower it's 50 meaning that it's not going to be rendered in here so let's go ahead and resolve that so find the drop- down menu uh first let's find the drop- down menu content let's see drop- down menu content so find constant drop down menu content like this and find where it says zindex 50 and change this to zindex 99999 like this and besides drop- down menu content one more thing that we also have to change is sub content so let's go ahead and find the sub content it's right here and let's write 9999 like this so here I wrote 49 and here I wrote 59 I don't think it really matters but I'm trying to stay true to my source code uh I think I just played around with this until it matched what I needed all right so we have the sub content we have the content and let's see if we have any more Z indexes here I think that should be it uh if not we're going to go back and resolve this don't worry if you're having trouble finding this you can always just copy it directly from my GitHub we didn't write this anyway it's just from shaty and UI great so now we can go back inside of the user item right here and let's go ahead and import everything we need from these newly installed packages so let's go ahead and let's import Avatar and Avatar image from at SL components ui/ Avatar like this and let's go ahead and let's import from components UI drop down menu we need the drop down menu itself we need a drop-down menu content we need the item like this we need the label and we need a separator whoops where is it so separator and we need a trigger there we go so we need all of these items from here and while we're writing import let's also import an icon we're going to need which is chevron's Left Right from Lucid react great so now that we have that go inside of user item and go ahead and extract user from use user which if you remember we can get from clerk so use user not user user but use user from add/ CL at clerk clerk react great so now we have the currently logged in user and now let's replace This Ti with a drop- down menu now let's write the drop down menu trigger so we have all of this imported let's give this drop down menu trigger an as child prop and now let's write a div with a roll of button and let's give it a class name of flex items D Center text- small padding -3 w- full and hover BG D primary with an opacity background opacity of .5 inside of it open another div with a class name of Gap dx-2 Flex items Das Center and a Max width of 150 pixels inside of that div it's time to render our Avatar which we imported recently and give this Avatar a class name of h-5 and w-5 and let's render an avatar image inside with a source of user question mark. image URL great so now that we have that let's go below this Alor and render a span element and go ahead and write user question mark full name and write our appost trick Josan so in my case it's going to be a my name my surname aposto s Joan like that and give this class name give this span a class name of text start font D medium and line- clamp D1 so it truncates the line if it's too long and let's say if we can already see that and there we go you can see it right here you can see how it truncated uh the lines right here perfect so now we want to add a little Chevron here so it indicates that we can click on that and open our dialogue great so let's go ahead now and let's go outside of this drop down menu trigger actually no sorry we are still inside of here so find this div which encapsu is the Avatar and the span and go outside of it and add a chevron's left right icon with a class name of rotate 90 like that margin left of two text- muted D foreground and h-4 n W-4 and if you revisit your side right now there we go you can see how you have a nice little up and down shivon right here and now let's actually write the content for this drop down menu so let's go ahead and let's write dropdown menu content let's give it some props so class name is going to be W8 let's also give it an Aline of start a line offset of 11 and let's give it Force amount option inside of it let's write a div element which is going to have a class name of flex flex-all space- y-4 so everything inside is evenly spaced and let's add a padding so we have a bit more room to work with and now we're going to have a paragraph here where we're going to say user question mark email addresses z. email address like like this and let's write a class name text- extra small f- medium leading dnone and text- muted D foreground great so now if you take a look and if you click here you're going to see your email address right here perfect so let's go ahead and let's continue and let's write uh some actions inside so what I want to do is go outside this paragraph here and open a div and give this div a class name of flex items D Center and GAP dx-2 and now let's go ahead and write a div class name rounded DMD BG D secondary and ping of one inside let's render an avatar with an avatar image again and let's give it a source of user. image URL and and let's give this Avatar a class name of h-8 and w-8 so let's see how that looks now there we go so we have our little image here perfect and now let's go ahead outside of this Avatar outside of this div encapsulating the Avatar and let's add a new div with a class name of space- Y whoops space- y- z-1 my apologies and and inside just open a paragraph with a class name of text small and line- clamp D1 and just render user question mark. full name appus add an S Jo like this and now it should have a little more options here so we have my email and my name and that this is my Jan right and now we're going to add a little log out button from here so let's go ahead uh outside of this div which is en encapsulating uh the uh the paragraph and outside of this div as well actually we can go outside of all divs all the way here at the bottom but still inside of the drop down menu content and just add a drop-down menu separator which is a self closing tag and then a drop down menu item which is going to have a sign out button well first let's add the closing one for this one like this and then inside we're going to have a sign out button and we can get the sign out button from the same place we got use user right here from clerk clerk react and let's go ahead and oops and let's go ahead now uh and let's just render log out and give this drop down menu item a class name w-o cursor Das pointer text- muted D foreground and let's also give it an as child option great so now if we take a look one more time when I click on here here's my email here's my name and I can click log out and I am back on the landing page and let's go ahead and log in with GitHub again there we go I can now enter Jan and there we go so this is fully working great and now we're finally ready to create our first action which is going to create our first document so now that we have some items inside of our sidebar it's time to enable this button that when we click on create a note we actually create a new note so let's prepare a couple of things first what I want to do before we start anything is install a package called soner so npm install soner like this and this is going to install soner and now you can go ahead inside of your app folder layout. TSX and go ahead and import so let me just do this at the top import poster from soner like this and you can just put it inside of this layout server component inside of the theme provider like this it's a self closing tag and give it a position of bottom center like this you can of course play around and modify it to what you like but this is the one I prefer great so now that we have this let's go ahead and let's create our schema but before we do that just confirm that inside of your terminal you have let me see this one is this okay you should have the MPX convex Dev running and you should have the mpm Run Dev running so just make sure you have both of those on and what we're going to create now is our schema so let's go ahead inside of the convex folder right here and create a new file called schema. DS like that and go ahead and import Define schema and Define table from convex SLS server and go ahead and import V from convex SL values and now expert default Define schema and let's define our t which is going to be documents so write Define table open an object and let's give it an option of title which is a type of string like that a user ID which is also a type of string this is where we're going to pass in the clerk user ID below the user ID at a Boolean is archive so v. Boolean we're going to use this for soft deleting of the documents go ahead and add a parent document this is going to be optional so v. optional like this and v. ID is going to refer to documents itself so we are going to create a relation with the very same document that we are creating so every document is going to be able to have its own parent document if we create recursive documents inside as you saw in the demonstration or they can just be all parent documents now we're going to have content which is going to be the actual value written inside of the written inside of the uh specific document and that's going to be an optional v. string like this and let's use cover image that's also going to be v. optional also a type of v. string besides cover image we're going to have an icon which is again an optional string and lastly we're going to have a Boolean is published which is going to be v. Boolean so published is going to me is going to mean visible to outside of uh to any guest right so you can share the URL as you saw in the demo if it's not published it's just private to you and now let's create some indexes for faster querying so index by user so we can query by the currently logged in user and we're going to use the field user ID for that and let's also create an index a combination of user and a parent document we're going to use this in our sidebar so by user undor parent and the values which we're going to work with our user ID and parent document there we go so we just defined our schema and now if you want to you can go ahead and visit convex dodev and log in here go inside of your dashboard and just select uh the project you're working with so notion clone in my case right here and I think you should already have a way to find a schema here uh if there we go so it says documents right here as you can see and I can click show schema and there we go this is the code that I've just written here and I can see it in my browser that's really really cool so that's how you know that uh your code from convex right here thanks to this terminal which is running as you can see for a second I had a wrong schema here but it doesn't matter like you can just keep npx convex Dev running and it's going to watch all the changes you write and as you can see it added table indexes and invalidated my schema and then I have that schema right here in my browser so I can see exactly this is correct and the convex server is working perfect so if you want to keep this open because now I'm going to show you how this data will appear here and you can of course play around and run some functions from here but we don't have any API functions yet and that's what we're going to create now so let's go ahead inside of the conx folder and create a new file called documents. and let's go ahead inside and first let's go ahead and let's import V from convex SL values and let's go ahead and let's import mutation and query from do/ generated server and let's also import Dot and ID from do/ generated slata model great so now that we have this we are ready to create our first mutation so let's write export const create to be a type of mutation and let's go ahead and give the arguments for this function so the arguments are going to be a title which is a. string so when we create a new document we're going to have to pass in the title of that document and we're also going to have uh an optional parent document so we're going to reuse this function if we are creating a child of another document or it can just be a completely new parent document so that's why we're going to write v. optional like this and then v. ID documents great besides this we're going to have a Handler function and that Handler is going to be an asynchronous function which is going to have the context and the arguments like this and then we can fetch the currently logged in user using context so con identity is going to be await context. out.get user identity and if we don't have the identity that means we are not logged in so an unlogged in unauthenticated user is trying to create a new document we won't allow that so throw new error not authenticated there we go and now let's extract the user ID from the identity so const user ID is equal to Identity do subject and now let's finally create our document so const document is equal to await context. database. insert and we are inserting documents and now let's define exactly what we're inserting so the title is going to be arguments. tile which we defined right here to be a type of string but we are missing a couple of stuff so we need a parent document if it exists ar. parent document if it doesn't exist this is just going to be undefined and that's completely fine because in our schema we said that parent document is optional and as you can say one of the options of that is undefined so this is completely working and we can reuse it and we're going to have to pass in the user ID which is user ID or you can just write a short hand like this and let's set is archived to be false and is published to be false as well and there we go no more errors and now we can just return the document from here and go ahead and save this and you can always check your terminal to see if there are any mistakes as you can see it's checking everything and there we go convex functions are ready so we successfully wrote this function and now let me just refresh my dashboard here and I think that you should already see your functions here there we go documents create so click on the functions here and as you can see this is your code and you can see how many invocations you have how many errors it has and how long it takes to execute that so that's why this convex is really cool because it almost breaks the barrier between what you write here and what you write in this little interface they have here really really cool and it makes it so easier to test it out whether it's working or not great so now that we have this it's finally time to for me to show you how you can actually add this uh inside a react component right and as you can see we are currently not using this but it's going to come in handy later great so make sure you save this make sure you confirm that you have that in your functions and now let's go ahead and let's go inside of the app folder inside of the main folder routes documents page. TSX right here so we have this button which says create a note so first let's check if we have used client we do great and now what we can do is ADD our create method so const create is equal to use mutation and now let's go ahead and let's import this use mutation so import use mutation from convex SL react and while we are here and it's not user mutation it's use mutation like a hook and while we are here let's also import our API and we get our API from at/ convex generat API there we go so now that we have that let's go inside of this create mutation and let's write API and as you can see we have type safety here so do documents dot create exactly what we've written in our documents page and let me just revisit it right here so it's exactly this documents and if I renamed this file uh where is it my convex if I rename this documents file into something else then the API would change it would be API do something else do create and same thing if I rename create so you don't have to follow this exactly as I'm doing it you just make sure that you know it's the same logic behind it and now that we have this create we can directly add that on click here if we want to but what I want to do is I want to create a little Handler so I can do additional actions on it like fire a toast unsuccessful creation and maybe redirect the user to that newly created note so let's write const on create like that which is going to be an arrow function and now let's write const promise to be create and as you can see it's expecting an argument and that argument is an object of our uh of uh our arguments and as you can see parent document is optional but title is required exactly as we've written in the arguments right here and that means we just have to pass in the title and for my code what I'm going to do is I'm going to pass in the title Untitled for every new one like this great now that I have this promise what I'm going to do is I'm going to add the toast from the toaster from sonar so let's go ahead to the top here and let's import toast from soner like that and let's go ahead and let's write post. promise let's pass in that promise and let's define the states so if we are loading we're going to write creating a new creating a new note if we have have succeeded so success we're going to say new note created and if we fail if we get an error we're going to write failed to create a new note all right so now that we have this let's go ahead and let's add this on click to this function sorry to this button right here so button now is going to have on click on create great so let's go ahead and expand this screen here let's see our data here in the conx so currently have nothing in the documents table Let me refresh this and when I click create note you can see how it says new note created and let's revisit and there we go we have a new document is archived is false is published is false title is Untitled and we have a matching user ID inside we also have creation time made for us and we also have the ID so great we successfully created our first API route so now I want to show show you how you can actually load this information now I showed you how to create it in react components but how do you load it well very simply let's go ahead and let's revisit our convex right here so where is it it's convex folder go inside the documents. DS and just for fun let's go ahead and let's add a new API endpoint here export const get and it's going to be uh a query and let's go ahead and for now we can just do a Handler I believe like this it's going to be an asynchronous function which is going to have the context it's not going to have the arguments because we didn't pass it and let's go ahead and let's do this thing so we're just going to go ahead and get the identity to make sure we're logged in and then what we're going to do is we're just going to write const documents to be await DB sorry context. db. query like this sorry context. dbquery documents and we can just leave it like this for now and return documents so this is a very simplified function of course we are not actually checking whether the logged in user is quaring its own documents we're just loading all documents but I just wanted to repeat how we do authentication here and later we're going to add the index here so great now that we have this let's go ahead and let's go into inside of our navigation so inside of main components navigation right here and let's go all the way to the top here we're going to remove this later but just for now I want to show you so let's go ahead and write con documents to be uh used query and we have to import use Query from the same place we imported uh use mutation so use Query from convex SL react and let's go ahead and pass in the API which we get from at SLC convex generated API and let's pass in the api. documents and as you can see it autocompletes the get function and there are no errors because we don't have to pass in any arguments and now that we have these documents right here let's go ahead and let's find the div where we actually wrote the paragraph documents and instead let's go ahead and write documents question mark. map because they have to load right but we are not going to do loading States now and let's get the individual document and let's go ahead and return uh let me just see I think I made a mistake here uh just a second let's just go ahead and let's see what we'll end up with here so let's just add a paragraph see here with a document dot title like this and let's see if this is going to break anything or not all right so return value is a query oh yes I think I forgot to actually uh write my uh write my entire query here so go back to documents doget and we have to write collect at the end I forgot to do that and now right here there we go you can see no more errors regarding the query instead it's missing a key which is document doore ID and now there we go no more errors perfect so just make sure that in your documents in this get function you add collect at the end perfect and now you can just leave the query as it is so this is where we defined the query so cons documents use Query API the documents get that is fine and then in the documents we just iterate over them perfect and now in your sidebar there we go it says entitled and look at this when I click create a new note instantly I have a new one so this is a realtime database meaning that this use Query function which we defined in the sidebar is also uh watching in real time all of the changes we have perfect so that's what I wanted to demonstrate you so now that we have this functionality to create a new note it's time for us to also create the same button inside of this uh sideb bar and it's time for us to also render these items in a nicer way and let me show you how you can clear your entire schema from here so you can just click right here and click clear table and just confirm and there we go instantly everything is gone and you can start all over again great great job all right so now let's go ahead and let's add this create a note button right here in the sidebar as well so first thing I'm going to do is go back inside of my convex dashboard and I'm just going to clear my entire table so I don't want to have anything in my sidebar like this so completely empty make sure you do that and now let's go ahead and let's go back inside of our navigation right here and let's go ahead and let's find uh our div where we have the user item right right here so find the div which is encapsulating the user item go below that and go ahead and write an item element which is not defined so if you save you're going to get an error but I already want to give it some props so we're going to have an on click option which is just going to be an empty Arrow function and we're also going to have a label which is going to say new page and we're going to have an icon plus Circle and plus Circle comes from Lucid react so just make sure that you import plus Circle from Lucid react so let me just collapse this props so you can see how this is supposed to look like so item is a self closing tag which has the onclick label and icon right here perfect so go ahead uh and save that and now let's go ahead and let's go inside of this underscore components where we have the navigation itself and create a new file item. DSX and let's go ahead and let's mark this as use client and let's ort con item like this and let's just return a div saying item and then we can go back inside of our navigation and we can import that item from dot SL item so let me show you where it is there we go so import item from do/ item the same way we did with user item great and now we can go ahead and save this and let's just see all right and you should no longer have that error in the dashboard instead you should have a little this which now says item Right Here and Now what we have to do is we have to fix this type errors so we are sending on click label and icon so let's go inside of the item and let's create an interface item props and I'm going to go ahead and add a label U which is going to be a type of string I'm going to add on click which is going to be a type of void and I'm also going to add an icon and I'm going to make it a type of lucid icon from Lucid re yeah and now let's go ahead and let's extract all of those props and let's map them to item props so we have label on click and icon great and now let's go ahead and let's go ahead and render the label here so already in your sidebar this should now say new page because in the navigation as you can see we send the label new page great let's go back inside of the item now and let's go ahead and give this div an onclick option of onclick let's give it a roll of button let's go ahead and let's give it a style adding left 12 pixels and I know you're wondering why am I using the style prop here well that's because we're going to reuse this item element later in the future to create a list of documents which we have and if you remember each document can have a child document which means that we're going to have to do some math to push it more to the left if it's a child right so you're going to see that logic later on so I just already want to prepare that inside of the style prop rather than class name because then we're going to have to remove the class name and it's just going to be complicated so let's go ahead and write an actual class name for the rest of the Styles here so we're going to have group mean- H is going to be 27 pixels text is going to be small padding on top and bottom is going to be one padding on the right is going to be three width is going to be full when we hover on it the BG is going to be primary slash5 it's going to be a flex box it's going to have its items centered and it's going to have text muted foreground and we're also going to bold the font a bit by adding font medium great and now let's go ahead and let's render this icon in order to render this icon we have to remap it to Capital icon sorry icon with a capital I because now we can use it as a jsx element so make sure you do this inside of your props so just remap it to icon with a capital I and then you can just safely render that icon just like this and that's going to render whatever you pass in so in this case we're passing in the plus Circle so this is going to render plus circle inside of here and now let's go ahead and give this icon a class name of shrink -0 height of 18 pixels margin right of two and text- muted D foreground excellent and last thing we have to do is wrap this uh label inside of a span element so let's go ahead and write class name truncate like this and there we go so let me just go back to the item uh I think that should be it for now so let me expand my screen now and there we go now we have a new a new page uh button right here you can see how when I hover it has a nice little effect and it has a cursor button on it perfect so now what we have to do is we have to enable that when when we click on it on this that a new one uh is created so let's go ahead uh and do that now so what I want to do is I want to go back inside of my navigation here and let's go all the way to the top where we add the documents query and what I want to add is the create function so const create is use mutation and let's go ahead and let's import us mutation from convex SL react and now let's go ahead and pass in the API which is going to be api. documents. create great now that we have this create function let's go ahead and all the way before we return something so after all of this functions for the Navar let's go ahead and write const handle create and inside of here we're going to do the same thing that we did inside of this main page so we're going to create a promise const promise is going to be create function and we're going to pass in the title of Untitled and then what we can do is add our toast from uh soner so let's go ahead and import soner so import tost from soner great and now let's find that handle create function here then we can do toast. promise passing. promise and let's give it a loading state of creating a new note let's give it a success state of New node created and let's give it an error state of failed to create a new note like this and now let's add this handle create in this uh item right here where we currently have an empty on click function like this and now let's see what happens so I'm going to refresh everything I have nothing inside of my documents here but when I click here there we go a new one is a appearing here so I can do it both from here and from here now and you can see I have four of them inside of my uh dashboard as well perfect so now we have our new page button working and now what I want to create is I want to extend this item component which we just created and give it a lot more functionality so it can be reused for both this button but also for this rendering of individual documents so I want each document to have its own icon I want to have different levels of the documents for example if this one is a child of this one it has to be indented a bit that's what we're going to use uh the where is it we're that's what we're going to use the style padding left for so we're going to double the padding if this one is a child of this one and stuff like that let's head back into the item component right here and first I'm going to do is I'm going to extend the props that you can accept so I'm going to add the ID prop which can be optional because there are some items which we're going to use like a button for a new page and some items which are going to be actual documents so they're going to have to have an ID because they're going to be inside of a map function so you're going to have to add their ID right so we know where to redirect and stuff like that and that ID is going to be a type of ID from convex undor generated data model like this so make sure you import that right here I'm just going to separate it all right and the way you write ID is open pointy brackets and just write documents you can see how we have type safety here besides an optional ID we're also going to have an optional document icon which is a type of string and we're also going to have an active uh Boolean prop we're also going to have expanded we're also going to have is search we're also going to have a level which is a number and we're also going to have have on expand so all of these are optional because as you just saw we just wrote when we created the first item uh we just created the basics is going to need but all of this else is optional right but we are going to use them uh to render the list of items uh great so now that we have that let's also go ahead and add all of those things here so we have an ID uh we have a label let's go ahead uh and go below the icon and let's add the active prop let's go ahead and add the document icon let's add the is search let's add the level and let's give it a default property of zero like this so even if we don't pass it the default level is going to be zero let's give it an on expand and expand it great so now that we have uh all of those um let's go ahead now and first thing I want to do is add the constant for Chevron icon so const Chevron icon is going to be a expanded question mark sorry expanded question mark Chevron down from Lucid react otherwise Chevron right from Lucid react so just make sure you have Chevron down Chevron right and Lucid icon from Lucid uh react package great now that we have the Chevron icon we're going to use it to render that next to individual item so it indicates to the user whether they have expanded this item to see its children or not perfect now let's go back inside of this div right here and let's change this padding left uh to be something more Dynamic here so as I said we're going to use the level here so if we have a level in that case what we are going to do so let's leave this as an else clause and go inside of here and open parenthesis sorry don't open parenthesis open vtic like this and inside we're going to render a special object where we're going to do level * 12 like that plus 12 pixels like this so padding left is going to check if we have an level and in that case it's going to multiply that level by 12 and it's going to add a 12 additional pixels which are these initial 12 which we have right here if it doesn't have a level it's simply going to have a petting of 12 as we had in the beginning so depending on the level that each item is rendered it meaning how deep or how deep of a nestly child it is of a specific document that's how much we're going to push it to the left right perfect so now that we have that let's go ahead and let's wrap this class name inside of curly brackets because we're going to make this dynamic as well and to do that we're going to use the CN util so make sure you import CN like I did right here and just open parenthesis and wrap the entire string inside of it great and then you can just press enter like this so it's collapsed you can clear let see okay these are my default classes and now let's add conditional ones which is going to be our active prop so if we are active we're going to do BG - primary sl5 and text- primary great and now let's go ahead inside of here and let's check if we have the ID so if we have the ID for that I'm going to use this uh double exclamation points and this is going to turn this into a Boolean so it's going to be true or false you can see when I hover it says that I can be either the type of ID documents or undefined but since I'm going to do a Turner check with it I have to turn it inside of a Boolean so let's go ahead and do it like this and in that case we're going to render a div which is simply going to render the Chevron icon which is this constant which we defined right here so Chevron icon let's go ahead and just not misspell it like this so it's this conditional one which is going to show whether we expect expanded or not on this document and let's go ahead and give this div uh a roll of button and let's give it a class name of h- full rounded D small hover vg- neutral D300 dark vg- neutral D600 and margin right of one uh great and let's give it an on click option of handle expand actually let's go ahead and let's just give it an empty Arrow function we don't have that handle expand yet and inside of this Chevron icon let's go ahead and give it a class name of h-4 W-4 shrink shrink d0 and text-- foreground sl50 great and now that we have this let's go ahead below that and let's add a conditional icon so right now we always render the icon which we send uh in the icon prop right but we're going to make this a bit different so we're going to go ahead and open if we have a document icon in that case we're going to go ahead and just render a div with that document icon and if you're wondering why can we render the document icon like this but we had to do the whole um renaming of the icon right here well that's because this icon is a type of uh Lucid icon as you can see but document icon is a type of string because it's just going to be an emoji right so that's why we can just simply render the document icon like this so if we have the icon we're going to render that emoji which the user assigned for its document otherwise we're just going to render a plain old icon which we initially assigned great and let's go ahead and give this div a class name of shrink d0 margin right two and text of 18 pixels like this great and now what I want to do is I want to go uh below this uh span right here and I'm going to check if this type of item is used for searching uh items so is search like that and inside we're going to do a kbd element and we're just going to render a span element right here and let's go ahead and let's just render uh span like sorry this command item like this and K unfortunately I don't know what is the shortcut for this little uh character if you want it you can just copy it from my source code or if you want to you can just write control or command like this so it doesn't really matter right this is just you know how it's going to look like there is no functionality behind this and let's go ahead and let's just give this span a class name of text- extra small and let's give this kbd a class name which is going to be a bit longer so ml AO pointer-events dnone inline Dash Flex height of five select Das none items Das Center g-1 rounded border BG D muted like this we're also going to have uh padding on both sides from left and right to be 1.5 font is going to be mono text is going to be 10 pixels font is going to be medium and text is going to be muted Das foreground and opacity well opacity is going to be 100% but I don't think we actually need this uh great and now that we have that uh well we're going to leave it at here for now there's one more thing that we have to create here but we're going to come to that a bit later and now that we've wrote all of that code you're probably going to notice that well our uh new page has not changed much right but let's see what else we can do now with all of these new prop which we created so I'm going to go ahead back inside of my navigation right here and I'm just going to expand this so I can still see my sidebar I'm going to find this item where I wrote new page and I'm going to go ahead and write an item again but this time I'm going to give it a label of search I'm going to give it an icon of search from Lucid react so make sure you import that from Lucid react right here I'm going to give it a prop is search I'm going to give it an on click which for now is just going to be an empty AR function and as you can see now let me just zoom in you can see how because I I wrote that is search thing we now have a nice little shortcut so the user knows in the future we don't have that yet but in the future user will either be able to click here to search through all of the documents or they'll be able to use a shortcut command key or if you're in Windows it's going to be control key right so let I just want to make this capitalized right here I think it's going to look a bit better uh if I do do that so let me just go back inside my item and let me find the K right here I'm just going to make it capital K because I just think it looks a little bit better there we go so you can see how now I have this search right here and I have a little shortcut for it here currently it has no functionality perfect so now what we're going to do is we're going to finally uh render this list of items here but before we do that uh one thing that I'm going to do is just add one more function between these actions which is still going to be a dummy function so go back inside the navigation and go ahead and copy this search and rename this to settings remove the is search prop and give this an icon of settings also from Lucid react so let me just see everything we need from Lucid react is chevron's left menu icon plus Circle search and settings great so now that we have that uh you should see you should be seeing uh a new item here so search settings a new page the only one that's functional is the new page right and now we're finally ready to create our new component which is going to be called document list which is going to be a recursive component which is going to call itself every time you expand on a user so it's going to be a very complex function and you're going to learn how to write recursive uh documents recursive react components in this one and we're going to leverage uh our schema which has the uh parent document it's going to be very interesting to see how that's going to work great job so far so now let's go ahead and let's create a new API endpoint which is actually going to be used for recursive calling uh for this sidebar right here so first I have an error in navigation let me just see what that is oh I have trailing white space so let me just fix all of those trunk is helping us with code quality here great and and now let's go ahead and let's go inside of convex let's go inside of documents uh right here and I'm going to go ahead and remove this get from here instead we're going to write a new one so expert const get sidebar is going to be a query it's going to have arguments of parent document which is going to be an optional value and if it is passed it's going to have to be an ID of documents besides arguments we're also going to have a Handler which is an asynchronous function which takes in the context and the arguments which we passed above and now let's go ahead and let's get our logged in identity so const identity is equal to await context. out.get user identity if we don't have the identity we cannot fetch this documents so let's throw new error not authenticated and I just look it shouldn't be normalized error it should just be throw new error like this great and now let's extract the user ID so const user ID is identity identity. subject and now let's go ahead and let's fetch the documents but we're going to do that using this indexes we have the index by user and by parent so we have faster in here so let's go ahead and write con documents to be await context. databasequery documents do with index the index we're going to use is by user parent let's go ahead and get the query function here and now you can write all of this in one line but I just collapsed it so you can see so I did not open anything here I just added a new line here and let's let's write Q dot equals user ID should be the currently logged in user ID and it also equals the parent document to be equal arguments. parent document which if not passed it's just going to be undefined and now let's also add a filter here let's also get the Q for query and again I'm just going to press a space here and let's write query equals query do field is archived is going to be false so we don't want to show any of the deleted documents or in our case archived and let's order all of those documents by descending and lastly let's collect them like that and now we can just return documents there we go and I have a trailing wi space here so let me just remove that space and there we go perfect we have our get sidebar function function now and now let's go back inside of the navigation component so it's located inside of the app folder mainor components navigation and from here we can now remove the used query from the convex react we don't need it and that means we can also remove this documents use Query we no longer need that as well and we can also remove uh this as well we don't needed so now that I removed this document list I just want to show you a little trick so if you if you're using trunk right we at the beginning of the tutorial we we installed an extension called trunk right here so as you can see because of it we have this improved code quality where it says that I have trailing whites space here and usually I fix that by deleting it manually myself but if you want to you can also go ahead and press command shift p or control shift p and go ahead and uh write settings and select the UI option right this and you can search for format on Save and you can enable this and then when you save there we go you can see how the entire thing is formatted right but I'm not going to use that for the tutorial because it changes just too much of the files so every time that I do something like this you're going to see me uh do it manually so let me just go ahead and find uh where I rendered this there we go right here so I have the trailing wi space I'm just going to remove it great all right and now inside of here let's go and save this and let's go inside of the documents file sorry inside of the item file so item right here and go ahead all the way to the bottom and let's add a skeleton for this one so item. skeleton is going to be a function item skeleton it's going to accept a prop level which is going to be an optional number and and let's just not call this uh item SK on like this great and inside just return a div like this and go ahead and give it a style padding left if it has a level open back Tex and write level * 12 plus 25 pixels because this is uh going to be a different element so uh we have to modify just a little bit otherwise it's just going to be plain old 12 pixels all right and inside of it we're going to render some skeletons so first let's go Ahad had inside of our terminal let me open a new one and just write npx shat cn- at latest add skeleton like this just add a skeleton component great and now inside we can go ahead and render this skeleton component from at/ components UI skeleton so and it's going to be a self- closing tag right so just self close it and make sure you import skeleton from at/ components UI skeleton great and go ahead and write a class name here h-4 W-4 and go ahead and copy it and write this one to be 30% like that and go ahead and give this a class name of flex Gap dx-2 padding U bottom and top of three pixels great so now we have the skeleton now let's go back inside of the navigation right here and let's render the document list component which we currently don't have but we're going to have it in just a moment so great save this file and you should get an error now go inside of the underscore components and create a new file document dl. DSX perfect go ahead and Mark this as use client and now what we're going to do is we're simply going to export document list export con document list return div document list like this go back inside of the navigation and you can just import the document list from uh _ document list great and now that you have this let's go ahead and let's create an interface document list props parent document ID is going to be an optional ID from at ATC convex generated data model a type of documents like that besides the parent document ID we're going to have a level because remember this is going to be a recursive function and we're going to have data which is going to be a type of the document schema so for that we can use the doc from uh very same import right here and the doc is going to be a type of documents as well and it's going to be an array of those great so let's go ahead and extract this props here so parent document ID and level which by default is is going to be zero and we don't need to render data here you're going to see why in a second uh all right and let's just assign this props so document list props like that and now let's go ahead and let's get our params from use params from next SL navigation so just make sure you add this import all right besides the params we're also going to have router so use router from next SL navigation all right we have that now uh and now let's go ahead and let's write const expanded set expanded to be use State and give it an empty fun empty object inside and let's import use state from react I'm just going to move that to the top as well and we're going to give a type of this use state to be a record which takes in the string and a Boolean like that perfect and now let's write a function on expand so const on expand it's going to take the current document ID which we are trying to expand and all it's going to do is set expanded prev expanded add all the previous expanded documents and find the individual document ID which we are trying to expand and just toggle the function so the opposite of the current state of document ID inside of the extended function great and now let's go ahead and let's uh add a query for our get sidebar API which we just created so cons documents is going to be use Query let's go ahead and let's import use Query so import use Query from convex SL react all right and inside let's render the API and I imported API from convex generated API and let's write API i. do.get sidebar like this and let's go ahead and pass in some props so we're going to passing the parent document to the parent document ID from our props right here great and now let's go ahead and let's write the on redirect function so const on redirect is going to take in the document ID which is a type of string and router. push SL documents the individual document ID we currently don't have this route but we're going to have it in the future perfect so now let's go ahead and let's render the loading function so when using convex uh it will never be undefined unless it's loading so we're going to do a check for that so if documents is undefined that can only mean that it's loading if it's truly failed or wasn't able to find anything is going to be null right but you can use the UN defined state to activate the loading state so let's write return let's open a fragment and let's go ahead and import the item component from do/ item so make sure you add the item like this and since we just added this little skeleton to it we can now use that as our loading state so item do skeleton like this and let's give it a level of level like that and then let's go ahead and let's write if level is equal to zero in that that case let's go ahead and render another fragment here with two item skeletons so let me just close this fragment like this and you can just go ahead and copy and paste this two times like that perfect so now we have our loading State here and now we're finally ready uh to render this list so change this to be a fragment and let's write the first paragraph here which is going to write no Pages inside so this is going to be if we click expand a document which has no children so go ahead and give this a style ping left if we have a level in that case open back and write level * 12+ 25 pixels or undefined like that and we're also going to have a class name here so class name is going to be CN from add/ Li utils so make sure you add that as well so CN go ahead and write hidden text small font D medium text- muted D foreground sl80 if it's expanded in that case go ahead and give it a class of last block otherwise if level is zero go ahead and render hidden all right so this is going to use CSS to detect if the if this is the only element inside of this entire fragment that means that there are no other documents rendered and we can safely uh display it right but if it's not last in that case it's going to be hidden by default all right and now when we are below that let's go ahead and render our documents so documents. map get individual document and let's go ahead uh and let's write a div with a key of document doore whoops document _ ID and inside let's render the item element and let's go ahead and let's give this it item and ID of document doore ID let's give it an on click function to Be an Arrow function which calls on redirect function and calls the document. ID inside so it knows what to redirect to a label is going to be document.title Icon by default is going to be file icon from Lucid react so make sure you import a file icon from Lucid react all right and now let's go ahead and let's edit a document icon of document. Icon let's give it an active prop of pam. document ID to be identical of document doore ID level is going to be level on expand is going to be an arrow function which calls on expand documentor ID expanded is going to be expanded documentor ID so we're going to check inside of that object and now we're going to do the recursive trick so in see every item can have children on its own guess what component we're going to use for that well this component that we are writing right now we're going to reuse the entire thing so what we can do is call this component document list it doesn't matter that we are writing inside of it we can call it inside but we're going to conditionally call it so we don't fall into an infinite Loop so let's write if this element is expanded so if expanded object which we hold in our state holds the document ID and the value is true in that case we're going to render the entire document list which we are just now creating and let's give it a parent document of document underscore ID and let's it a level of level + one like this perfect so now that we have that let's go ahead uh and let's check if that is enough for us to actually see something and there we go look at all of my items here perfect right now the expand function is not working as you can clearly see and when I click on any of these documents I lead to a 404 because we don't because uh we don't have that page yet but if you look create a new note you can see how it's immediately right here at the bottom perfect so that's exactly what we wanted what I want to do now is an option to delete a document to create a new document and to actually expand documents when we click here instead of redirect to them so let's go ahead and quickly fix this unexpanded thing so go back inside the item. DSX right here and let's go ahead and write a function here so I'm going to do it above this Chevron icon uh const handle expand and it's going to take an event which is a type of react. mouse event which comes from HTML div element and has a mouse event inside it's an arrow function and all it's going to do is prevent the propagation from it and then it's going to hold on expand but since it's optional we're going to add a question mark Dot and then execute the function and then add this handle EXP p uh and let's see where we have to assign it uh let me just find there we go it's right here so find this uh ID tary right here and this Chevron icon and we have this on click right here and just add it here there we go and let's see how that looks now so I'm going to refresh and now when I click here you can see how it says no Pages inside isn't this really really cool because none of these items have any children inside of it but now we're going to create a little plus icon here which is going to allow us to create a new document inside let's head back inside of our item component and let's go all the way to the bottom where we render this search thing and go below that and go ahead and add another uh check if we have an ID and if we have an ID go ahead and render a div which is going to hold two actions one to add a child inside of of that document and the other is going to be a drop down where we can delete the document so write ml- aouto Flex items D Center and GAP dx-2 and now inside let's go ahead and let's create another div which is going to render the plus icon from Lucid react so make sure you add this plus from Lucid react here and go ahead and give this plus a class name of h-4 W-4 and text- muted D4 ground and give this div a class name of opacity -0 group- hover opacity 100 h- full ml- AO rounded Das small on Hover BG is going to be neutral D300 but on dark hover BG is going to be neutral D600 so just make sure that when we do this group hover we actually have a group inside of our main div and we do right here so when we hover on the item only then are this additional actions going to show so let's see that now you can see no plus icon now but when I hover you can see I have a little plus icon here and it's only visible on the one I'm currently looking at perfect so now let's actually create an action that when we click on this we don't get redirected but instead a child document is created so let's go ahead and let's create that function so I'm going to go ahead to the top here just below the handle expand and let's write const on create which is again going to take an event which has react. mouseevent HTML div element and mouse event and let's go ahead and check if we don't have an ID we can just break the function otherwise let's go ahead and let's create a mutation so in order to create the mutation first thing we have to do is add it right here to the top so let's write con create to be used mutation and let's go ahead and import use mutation from convex react so import use mutation from convex SL react and let's go ahead and add our API from convex generated there we go so API from convex generated API make sure you have that and then api. documents. create like this perfect and now we can go ahead and continue with our own create function here at the bottom so let's go ahead and add a promise const promise is going to be the create function the title is going to be as always Untitled but this time we're going to pass in the parent document to be the ID of the current item which we are doing this in so we want to create a child with the parent to be this ID perfect and now that we have that let's write dot then let's get the individual document ID if we are not expanded in that case call the on expand optional function like this and router. push we don't have the router so let's just quickly add the router here const router use router from next SL navigation make sure you import use router from here all right and now let's go ahead and just write router. push documents document ID like that perfect and now let's go ahead and let's add our toast from the soner package so I'm going to go here and import toast from sonor and now we can do toast. promise passing the promise which we just created a couple of lines above here and as always let's give it some states so loading state is going to be creating creating oops creating a new note success state is going to be new note created and error state is going to be fail to create a new note and now that we have this on create right here let's go ahead and let's assign it to this new uh div which uh should have the roll button and on click to be on create like this and one thing that I'm just going to quickly do I'm going to go back to this uh on create function uh and oh I forgot to stop propagation here so let's just do event. stop propagation while we're here and let's also just comment out this router. push because all it's going to do right now is show a 404 page right so let's just hide that and now let's go ahead and try this out so I'm going to refresh everything I'm going to go ahead and click on plus here and look at this I can do this infinitely we successfully have children and you can close them you can open it and you can see how it dynamically loads them inside and you can expand the screen right here and you can see how now you have more space to work with you can create as many of these nested documents as you want amazing amazing job and you can collapse each and every one of them you can try and play around just don't spam too much you know these are recursive functions and you don't want to create too much band with uh on your conx account uh great amazing amazing job and now that we have this plus I icon it's time for us to actually create additional actions to delete the uh entire document and then we're going to create a little Trash Can where we can restore all the archived elements so now let's go ahead and let's create additional actions next to this plus icon which is going to be used for soft deleting or archiving a specific document so the first thing I want to do is I want to go back inside of our convex folder inside of the documents. Cs right here and inside we're going to go ahead and we're going to add a new API endpoint here called archive so let's go ahead and write export con archive which is going to be uh not a query my apologies is going to be a mutation which is going to accept arguments of ID which is a type of v. ID documents besides arguments is also going to have a Handler which has the uh which is an asynchronous function after all and it holds the context and the arguments which we passed above so first things first let's go ahead and let's get the identity we can just copy that from this right here and let's also immediately extract the user ID so I'll just copy and paste that here so we save some time and now let's go ahead and let's attempt to fetch the document using this ID so let's write const existing document to be await context. database. get arguments. ID and if there is no existing document in that case we can just go ahead and throw new error which is going to say not found and then let's go ahead and check if the user ID of the existing document so this user ID which we have in our schema is matching the currently logged in user ID because if it's not we cannot modify this document right so let's go ahead and write if existing document. user ID is not identical to the current user ID which we just extracted from the identity subject let's go ahead and throw new error unauthorized so it's not that we are not uh not authenticated is that we are unauthorized to modify that document and now that we have that let's go ahead and let's change uh the document so const document await context. database. patch ARS doid so that's the one we want to patch and the only thing we're going to change is is archive to be true but there is one more thing that we need here so for now we can just return the document but what we actually need to happen is that when we archive uh a specific document we also want to check for all of its children right if I archive the parent I expect that all the children are archived as well because well you can know decide the logic for yourself but this is what notion does so I'm just trying to stay true to the original and it kind of makes sense because this ones should not exist without the parent itself and you might be thinking well it's unconvenient that my uh children get uh deleted as well but think of it in another way what if you want to get rid of all these things right it will be annoying that you have to go individually and delete everyone it will be nice that we can just delete the parent and then everyone everyone else will get deleted as well so that's what we're going to use that logic for it so besides editing this document by ID we need to create a recursive function which is going to go ahead and try and fetch the parent of each document and see uh and recurs delete all the children of this document so let's go ahead and let's write const recursive archive inside of this function just above this patch function which is going to be an asynchronous function and it's going to accept document ID which is a type of ID documents and you can get this ID from our _ generated data model right here perfect so now that we have that let's go ahead inside and let's go ahead and fetch the children so con children is await context. databasequery documents with index so we're going to use index for faster search here by user and by parent let's go ahead and let's get the query and let's go ahead and write query do equals user ID to be user ID and equals parent document to be document ID which we pass in the recursive function and lastly let's just go ahead uh and let's write do collect like that perfect and now that we have that we have to create a for Loop which is going to to iterate over all of that children and repeat the same function you might be wondering well why are we not not using a for each or map well that's because we're going to do a promise inside of that so if you if you're familiar with that map and for each you know that they cannot do promises inside of them you cannot do asyn a weight inside of a four four each or map for that you have to use the good old for Loop so let's write it manually for constant child of children we're going to do a wait context. database. patch Child ID is archived true like that so we're going to get all the children from this document and we're going to Archive them as well perfect and let me just fix the uh which errors do I have here it says trailing wi space but we're going to continue developing right now just to see see uh if it's something else great so after this we're going to go ahead and rerun the entire function gate so await recursive archive Child undor ID so we create a loop here there we go so no more errors here so let's take a look at what we do again so in our uh archive function what we do is we pass the argument ID so this is the ID of the document we want to delete or in our case archive we check the identity and we attempt to fetch that parent document which we trying to delete if it doesn't exist we throw a not found if the matching user ID is not true in that case we are unauthorized to modify this now let's jump over this quickly and then what we do is we archive that document so we use the patch function followed by the ID we are trying to modify and we modify its data inside of this object by setting is archived to be true and now what we do well we don't do it yet but we're going to do it right now before we return the document we're going to call recursive archive and we're going to pass in the ARs do ID so this very same argument that ID we passed so after we modify the main document to be deleted we pass that ID instead of a recursive function which let's see what does it attempts to fetch all children which have that ID as their parent document and then we run a four Loop over all of those children and we change them to be is archived to true as well but we're not done here right because we have to check every single child one more time to confirm that they are uh that they don't have uh any children as well so that's why we have to repeat this uh recursive archive inside of itself to go to the end of the nested child and that way we know that when we archive this document because of this function and everything else will be archived as well perfect so that's what we want to do with our uh archive function we can leave it like this for now and now we are ready to create our uh UI for archiving for that we have to head back into our item component so go inside of app main components item. CSX right here and I'm just going to leave this un uncommented for now we we'll notice it later uh and the first thing I want to do is go right here where we have our own create function as you can see we created a div here which looks like it's supposed to hold more elements because we add a flex box and we add some item Center and a gap between elements but right now we only hold one element inside that's what because we prepared to hold multiple elements inside so let's go inside of it and instead of a div let's add a drop- down menu from add/ components UI whoops not the separator but the drop- down menu itself so let's import that from add/ components UI drop down menu like this and let's go ahead and let's immediately import everything we need from the dropdown menu so I started some imports here just make sure you don't accidentally import them from radex because you're going to think that you did something wrong with the code when it's just an import so you should not have radics imported anywhere inside of your code except in the UI folder but you don't write those components anyway these are all from chatsi and UI great so we're going to have to need drop down menu we're going to have to need uh the trigger so let me just find uh where is the trigger here drop down menu trigger we're going to need drop down menu content so let's get that we're going to need the individual item so drop down menu item uh and I think that's going to be it yes so only this are the ones we need and now we can go back uh to that div where we started with the drop down menu and let's add a drop- down menu trigger now which we have imported and let's go ahead and let's give it a prop as child and let's also go ahead and give it an onclick option where I simply want to get the event and I want to say event. stop prop ation so it doesn't redirect us right and let's go ahead and let's just indent this let's fix the trailing wi space and now let's go ahead and let's open a div here and let's give it a roll of button and let's go ahead and let's render more horizontal from Lucid react inside of it so let me just show you where I added that right here more horizontal from Lucid react perfect and now let's go ahead and give this some Styles so so class name is going to be opacity -0 but when we do a group hover we're going to have opacity to be 100 H is going to be full ml is going to be Auto rounded small hover BG D neutral D300 on dark hover is going to be BG D neutral D600 and let's give this more horizontal a class name of h-4 W-4 and text- muted D foreground and I think that already we should be start seeing something there we go you can see how we now have this little options button and when you click on it it should not redirect you to any page but if you click directly on this it should redirect you to 404 but because we stop propagation on the drop- down menu trigger it doesn't do that right and now what we have to do is write the content so this actually opens something so let's go ahead and continue developing that so outside of the drop down menu trigger let's add drop down menu content we already have that imported and let's go ahead and give it uh a class name uh of w-6 let's give it an align option of start aside of right and force Mount now let's go ahead and let's go inside and let's write drop down menu item and let's give it an on click for now to just be an empty Arrow function and inside let's write a trash icon from Lucid react and make sure you have the trash imported from Lucid react like that give this trash icon a class name of h-4 W-4 and margin right of two and let's go ahead and write delete like this below the drop down menu item let's go ahead and let's add drop- down menu separator uh which is going to be a self closing tag like this and let's write div oops and let's write last edited by and in here we're going to get the user which we don't have yet so let's go ahead and do that let's go ahead and go to the top here and just add const user from use user and we can import use user from clerk so let's import use user from at clerk clerk react now that we have our user we can go back inside of this drop- down uh menu content and let's write Last ated by user full name like that great and let's give this div a class name of text- extra small text- muted D foreground and padding two like that and let's see if we did any mistakes if this is going to work not so I'm going to refresh everything and I'm going to go ahead and click here and there we go oh I now have my little delete button here and it says last edited by my name right here perfect so what we're going to do now is we're going to create the button uh here that when we click on this it actually archives the entire thing and since we have the API ready for it all we actually have to do is just add a mutation for that so let's go ahead to the top here we already have the create mutation so below that add const archive we use mutation api. documents. archive and now let's go ahead and let's add const on archive which is going to take an event of react. mouseevent HTML whoops HTML div element and mouse event and inside we're going to go go ahead and write if there is no ID just return the entire thing I mean break the function and then const promise is going to be archive and passing the ID just like that and now let's go ahead and call our toast so toast. promise it's going to get that promise on the loading it's going to say moving to trash success is going to say note moved to trash and error is going to say failed to Archive note great and now that we have this on archive well one thing I forgot to do was stop propagation with the event so let's first do that event. stop propagation here at the top and now let's copy this on archive and let's finally assign it to this empty on click with the drop- down menu icon which holds the trash icon and the delete right here on archive great and let's try this out so what I'm actually going to do first is I'm going to go back inside of my uh convex dashboard and I'm just going to clear the entire table so it's easier to look at what we actually have right here and it looks like we have this little bug here we're going to revisit that uh but for now I'm not going to focus on that now but let's create a new page here and let's go ahead and see here as you can see it says is archived false let's go ahead and let's try and delete it and there we go it says note move to trash and let's see right here is archived is now true perfect and let's see what happens if I create a new page and inside of it I create a couple of children like this as you can see now I have a bunch of this new documents which are none of them are archived and if our recursive function is working correctly when I deleted the parent all of the other children should get archived as well let's see and there we go all of them are archived perfect and if you're wondering how come they are not rendered just because they are archived well let's revisit our API right here where we have the get sidebar function as you can see we have a filter here which ensures that it only shows those that have is archived on false which means that all of these here which have it on true are not going to be shown here so the first thing we have to do next is just fix this little issue where it says no Pages inside here I don't know why that's here uh and then we're going to have to create a little trash box here so we can safely recover our files if needed great great job so let's go ahead and let's try and resolve this little issue that we have here so basically when I have no documents that should be rendered here this shouldn't be rendered as well so let's go ahead and first thing that I want to do is revisit our navigation component so let's go inside of app main components navigation right here and let's go ahead and let's find where we render this documents list so we render it right here let's just go below it and let's add another item item here uh and let's go ahead and let's give it an onclick to be on create which we already have or is it handle create do we have it it's called handle create all right so handle create let's go ahead and let's give it uh an icon of plus from Lucid react and a label of add a page so we're going to have two ways to add a page here and I just added this plus icon from Lucid reacts to make sure you have that uh let's see okay it looks like it was that great so I just need another item on the bot on the bottom here perfect so yes just like in notion we have two ways to add a page either from here or from here right and let's see if now when I delete this and when I delete this there we go we no longer now have that no Pages inside issue because that is the last element perfect so looks like that's solved our issue uh what we have to do now is a little element below that which is going to be called trash so in order to do that first thing we have to do is go inside of uh our terminal and let's go ahead and write npx sh cn- at latest at popover let's go ahead and wait for this to install and then let me just zoom back in let's go ahead below this item right here and let's add that popover component so before we do that first thing I want to do is add everything we need from popover so let's go ahead here and let's import from s/ components UI popover let's import the popover itself the popover trigger and the popover content like that perfect and let's go all the way down here now and let's find where we need to add that or is it okay so here is I add the page and now here we write popover then we add a popover trigger and let's give this uh a well we didn't actually have to uh I I thought we might need to add as child but I don't think we do let's just do a class name here with w- full and margin top of four and inside let's add an item with a label of trash like that and an icon of trash as well and let's just go ahead and import trash from Lucid react like this and it looks like we are missing uh on click here in the item but I'm I think that that actually doesn't have to be required so let's go ahead inside of this item props and let's make this on click optional like that and seems like there are no errors here great okay there are no errors here and there are no errors here anymore perfect now below that let's go ahead and let's add popover content let's give it a side and let's do dynamically if we are on mobile it's going to be bottom otherwise it's going to be right like that and let's also go ahead and give it a class name of padding Z and W d72 and for now let's just write a paragraph which says trashbox and let me just fix the trailing wies space face all right and let's see if we have anything here there we go looks like our trash boox uh is right here and just one thing that I want to do is I want to go inside of the popover and I want to change its Z index so let's go ahead inside of components UI pop over right here and let's find this Z index here so this is the content let's change this to uh 99999 like that and I think that's all that it is to change here great so we just ensure that it doesn't overlap with any of the other elements we're going to have and everything still looks the same perfect so now what we have to do is actual component for the trash boox so first thing that I'm going to do is add an API route to fetch everything that is archived that should be rendered in the trash can right so let's go inside of convex right here let's go inside of documents uh and let's go all the way to the bottom here and let's write export const get trash that's going to be a query and it's just going to have an asynchronous Handler which holds the context and let's just go ahead and let's copy this identity thing that we always have so just add that to Handler so we get the identity from Context out to get user identity we check if we have it and we extract the user ID and now let's go ahead and let's write con wants documents to be await context. databasequery documents wi index by user query query equals user ID to be user ID and let's add a filter which also has a query and let's just simply write um query equals query. field is archived is archived uh is true like this so we only fetch our uh documents by using this index to confirm that it's made by our user ID and then we filter so that it only shows the ones that we deleted let's order by this sting and let's collect all of that information and let's return documents uh all right like that perfect let's let me just fix the trailing wi space Here and Now what I want to do is I want to create a restore mutation right so similar to what we did uh with archive now I want to create a function to restore them to bring them out of trash so export const uh restore is going to be a mutation which has arguments ID is a type of VI ID documents and Handler is an asynchronous function which holds the context and the arguments let's go ahead and copy and paste uh this identity thing right here all right and now let's attempt to fetch the document so con existing document is equal to await context. database. getet arguments. ID if we don't have the existing document we can return sorry we can throw new error not found and now let's check if the existing document has a matching user ID so if existing document. user ID and let's just not put an exclamation point here my apologies so if the existing documented user ID does not match the current user ID that means that we are not authenticated to restore this so throw new error unauthorized all right and now let's go ahead and let's do a very simple uh HST document a wait uh actually let me just see I think this is not going to do well uh just a second right so what I got confused with is that I remember that we can technically uh recover a document which has a parent right so we have to create a special Logic for that so let's go ahead and let's check if our document so if our existing document has a parent document in that that case let's go ahead and let's write consp parent to be await context. database. getet document. parent document sorry existing document. parent document like that let me try and expand this even more all right so if we have a parent let's check it if parent is archived so even if the parent is archived let's go ahead uh and let's change some things so in order to do this we first have to Define some options here so const options are going to be partial document documents like that and that's going to be an object and this is not partal it's partial like that and let's give it is archived to be false and so if is parent is archived in that case options. parent document is going to be undefined like that all right and now let's go ahead ahead and let's just do await context. database. patch arguments. ID and options like that and let's return uh the existing document like that great uh and now we have to do a recursive restore the same way we did with recursive delete right so let's go ahead Above This options here and let's write const recursive restore is going to be an asynchronous function which takes in the document ID which is a type of ID of documents and let's go ahead and get all the children so con children are await context. databasequery documents with index uh we're going to use by user parent let's get a a query query equals user ID to be user ID and it equals parent document to be parent document uh let's see if we uh no not parent document it's document ID my apologies all right and let's just collect that and now let's do a four uh loop here so four child of children await context. database. patch Child ID and let's give it his archive to be false so we unarchive it and now let's do await recursive restore to be Child ID great and now that we have that uh let's go ahead below this and let's write recursive restore to be arguments. ID perfect one more mutation that we have to write is the actual remove forever mutation and that's going to be a bit simpler so let's just write export const remove to be a mutation which takes the arguments and an ID of wi. ID documents Handler is an asynchronous function which has the context and the arguments and let's just copy and paste the identity thing with the user ID from our previous function like this so identity check for the identity and extraction of user ID and let's go ahead and let's fetch the document we trying to remove const existing document await context. database. getet arguments. ID if there is no existing document throw new error not found and if existing document men. user ID is not identical to currently logged in user ID in that case we can throw unauthorized like this great and now that we solved that we can just simply go ahead and do const document to be await context. database. delete arguments. ID like this and return the document perfect uh and I think that uh should be it and let's just change this one so go back inside of this um inside of this restore function here and I just want to do this one simple thing I don't think it really matters but we returning this existing document which we fetch all the way here right so we don't actually modify that element even though I think this should work what I want to do is just just cons document here and then just return that document instead like this very simple uh great so now that we have that we are finally ready to create our trashbox component let's go back inside of the navigation so app folder main components navigation right here and inside we have a paragraph inside of this popover and the popover content we wrote a paragraph for trashbox let's change it to be an actual trash box like this if we save of course we're going to get some errors so let's go inside of this underscore components and create a new file trash uh dashbox DSX let's mark this as use client and let's export con trashbox to very simply return a div saying trashbox like that go back inside of navigation and import the trash box from slash trash dashbox and there we go we no longer have an error and now we can fully Focus uh on this right here so let's go ahead and let's add some hooks first so const uh router whoops const router from use router from next SL navigation con forams use perams from next SL navigation con documents is going to be use Query which we have to import from convex react so use Query and let's add use mutation while we are here from convex SL react and let's add the API from at/ convex generated API so for the documents we're going to use api. documents. getet TR that's the function we're going to use now let's add the restore mutation so con restore is use mutation api. documents. restore the function we just wrote and and let's also do remove con remove use mutation api. documents. remove great now let's go ahead and let's add a search State because we want to be able to search through the entire trash box so con search set search whoops set search from use State and let's give it the default value of an empty string and make sure you import US state from react and now let's go ahead and let's write const filtered documents to be documents question mark. filter let's get the individual document and let's return document. title. two lower case do includes search. to lowercase as well so a quick little function to filter out documents using this search state right here and now let's just add a const on click function to get the individual document ID which is a type of string and let's just do router. push back tis documents document ID so that page that we don't yet have uh all right and now let's go ahead uh and let's add two more functions uh so functions for ReStore and for delete so const on restore is going to have an event which is a type of react. mouseevent HTML div element mouse event as well and it's O mouse event and it's also going to have the document ID which we are trying to restore which is a type of ID and you can get that from convex generated data model and it's going to be an ID of documents like that uh great and now let's open this Arrow function here and let's stop the propagation from the event and let's write const promise to be restore and let's pass in the ID to be document ID and now let's get our toast from soner so import toast from soner and let's go ahead now and let's write post. promise let's pass in this promise let's give it a loading state of restoring note uh let's go ahead and give it a success state of note restored and let's give it an error of failed to restore note there we go and now let's go ahead uh and let's copy and paste this entire thing and let's rename it to on remove and this one is not going to take the event so it's just going to take a document ID so no need to stop propagation here and let's just change this promise to not use restore but instead to use remove by document ID and this is not going to be restoring note it's going to be deleting note and this is going to be note deleted and this is going to be failed to delete note uh great and now let's just do if perm. document ID is equal to document ID so if we are looking at the document which we just deleted forever uh we want to redirect back away from that document otherwise we're going to get an error because we can no longer load that page so we're going to do router. push SL documents just like that uh great now that we have that let's go ahead and let's add our loading State here so if documents is undefined that means it's still loading so we can go ahead and return a did with a spinner component so import this spinner from add/ components spinner give it a size of large and give it a class name of h- full Flex items D Center justify Dash Center and padding off four uh great uh so now what I want to do is uh actually show the list of our items in trash here before we do that let's go quickly inside of our terminal here and let's go ahead and write npx chat CN UI at latest add input so we're going to need the input component for this one great make sure you have that let's Zoom back in and let's give this div a class name of text small let's open a new div with a class name of flex items D Center Gap dx-1 and padding of two let's render these search icon inside we can get the search icon from Lucid react so make sure you import search from Lucid react let me just move it here uh where are we uh okay right here and let's give this search icon a class name of h-4 and W-4 and below that let's add our new input component which we just added and that's located at s/ components uiinput and let me just show you that right here there we go make sure you add that perfect and let's go ahead and give this a value of our search State let's give it an unchange to take the event and let's just do set search to be event. target. value and let's give it a class name of h-7 padding X of two Focus D visible ring- transparent and v- secondary and let's give it the placeholder of filter by Page title all right and now let's go outside of this div and let's open a new one and let's give this one a class name of margin top 2 px1 and pb1 and let's add a paragraph here no documents found and let's only show that paragraph if there is nothing else rendered inside and we can do that using C CSS so let's add a class name hidden by default and it's only visible if it's the last element inside of this list like that and it's going to have a extra small text it's going to be centered text as well and it's going to be muted for ground color and padding bottom off to let's see if we can already see any of those let me just refresh this let's click on this trash icon and there we go you can see how it loads for a second we have a little search here and it says no doc docents found here perfect and now we have to render the documents inside so let's go ahead and do that so just below this paragraph right here go ahead and open filtered documents question mark. map get the individual document and render a div and let's give this div a key of document doore ID all right let's give it a roll off button let's give it an onclick to be uh an nrow function which calls on click and documentor ID and we have this on click defined right here so when we click on it we're going to redirect to it all right and then let's go ahead and give it a class name of text small rounded small W po power BG primary sl5 Flex items D Center text primary and justify between and inside let's go ahead and let's render our span element which is going to render the document title all right and let's see if we are already seeing that here there we go look at all of our deleted items right here and if you click it should lead you to a 404 page because we don't have it yet perfect so now we're going to style this a bit and we're going to add some actions beside it to restore it back or to delete it forever great so go uh below this span right here and create a div here with a class name of truncate in case the title is too long p-2 oh my apologies this is actually a class name that I wanted to give to this span like that and for this they just a class name of a simple Flex item Center and inside open a div which is going to have an onclick function get the event and call on restore pass in the event but also pass in the documentor ID and give this a roll of button and a class name of uh rounded small padding to and hover BG neutral 200 great and inside we're going to render the undo icon from Lucid react so make sure you import undo from Lucid react let's see how that looks now when I click on trash okay we have a big undo button on the side here so now let's just style that button a bit so let's go ahead and give it a class name height four with four and text muted foreground like that perfect and now go below that and add a new div uh which is going to render the trash can from Lucid react so let's go ahead and import trash from Lucid react all right let's give it a class name of height four with four and text muted foreground and let's go ahead and give this Dave a roll of button and the class name of rounded small padding to and hover BG neutral 200 like that and now we should have two little icons here there we go the trash icon and the restore button and I think our restore uh should already be working there we go you can see how when I restore they all become individual items because they don't have a parent right and let's go ahead and delete them individually and we can do that as well perfect and let me just delete absolutely everything from my table so I can try this out even more so I'm going to try this I'm going to add a new parent inside there we go so I have three elements I have nothing in trash I'm going to delete the parent like this and I think that this is the parent the first one that was deleted and I'm just going to attempt to restore that one and everything else should be restored as well let's see there we go everything else is restored inside perfect so what's left to do now let me have something deleted is the actual delete function right now it doesn't do anything beside actually redirect me to that page um so that's what I have to do uh next so to wrap it up we have to add the complete remove function and in order to do that I don't want to assign it to just be on click I want to have a nice little model so the user can confirm that they want to do this because this is not a reversible action for just archiving we don't need a confirmation model because they can always go to the trash boox and bring it back but for the absolute complete removal from the database I think it's nice that we create a little confirmation model for that so let's go inside of our terminal and let's install a package MPX Shad cn- at latest ADD alert D dialogue go ahead and install this package and after that is done we're going to create a reusable component called confirm model so let's go inside uh of our components folder and create a new folder called models and inside create a new file uh confirm dasm model. DSX like this let's let's go ahead and Mark this as use client and let's go ahead and let's import everything we need from s/ components UI alert dialogue which we just installed so we need the dialogue uh itself we need the dialogue action we need the cancel action as well we need the content we need the description putter Adder whoops so I alert dialogue header and title and last one trigger so all of this inside perfect and let's go ahead and write an interface for this so interface confirm model props is going to accept children which are react do react node and we're going to have the on confirm option which is going to be well whatever we want depending on the use case and now let's export const uh confirm a model and let's go ahead and assign this props here so confirm model props and let's extract the children and the on confirm great and now let's go ahead and let's just do a return of the alert dialogue let's add the alert dialogue trigger and let's render the children inside so the trigger is going to be whatever we want it to whatever we wrap the confirm model around around that's going to be the trigger button and let's add the on click which just simply is going to call event. stop propagation and let's give the as child option perfect now inside let's add the alert dialog uh content great alert dialogue header and let's go ahead and write the alert dialogue title which is going to have the title of R you app absolutely sure and below the title we're going to add the alert dialogue description which is just going to confirm to the user that this is uh an irreversible action so this action cannot be undone and now let's go uh outside of the header and let's add the alert dialogue footer and uh let's just go ahead and write the alert dialogue cancel button here whoops it's not going to be a self closing tag like this and let's go inside and let's just write cancel and let's give it an on click event event. stop propagation like that and now let's add the alert dialogue action and this one uh let's also give it an ending tag and this one is going to say confirm like this and now let's just go ahead and write a function handle confirm so const handle confirm is going to be an event of react. mouseevent HTML button element and mouse event like this let me just put this in a separate line so that's going to be our event and let's do event stop propagation and let's do on confirm right here and then we can use this handle confirm to be an onclick for this action and now we can go back inside of our trash box here and we can wrap this entire thing inside a confirm model like this so make sure you import confirm model from add/ components models confirm model wrap the entire div around it and just give this one on confirm uh to be a function which is going to uh remove the document so it's going to be an arrow function function which calls on remove document ID like that and we have on removed right here perfect now let's go ahead and try that out so I have three items in the database uh let's see two of them are active one is in the trash if I click here oh it looks like I have a model but as you can see it's not the Z index is not correct so before we try this out let's actually fix the Z index first so we have to go inside of uh components UI alert dialogue and let's find the Z index for the overlay and change the 50 to 5 9 so 1 2 3 one two like that and let's copy and paste this so we did it for the overlay and now let's also just do it for uh the content so let's just find Z Index right here there we go let's add it here let's see if we have the index anywhere else I think that's it yeah great so just those two places and let's try this again when I click here there we go you can see how now it's at top uh and let's just not click at anything else while we're doing this and let's click confirm and there we go note is deleted and it's no longer in trash and if I look at my database I only have two items perfect and let's go ahead and try this one more time there we go I can now completely remove my entire database just by using UI amazing amazing amazing job so now let's go ahead and let's wrap up our sidebar items right here so we have our working uh Pages here we have the working trash but we don't have the working settings and the search bar so before I get onto that I just want to bring to your attention that currently when I expand here you can see how for a second it's like I have a blank space and then I have something rendered instead of that I was hoping that the skeleton would show because we did create an item skeleton right here in our app folder main components item you can see that right here we have a skeleton right here but looks like something's not working so what I want to do is go inside of components UI and find the skeleton right here and it says that this background is muted so let's go ahead and let's change that to be BG primary sl5 like this let's change that to this and let's see if that improves anything there we go you can see how now for a second it's very subtle but if you look at it I have like a like a small little skeleton inside instead of just a blank space and in my opinion that looks better perfect so now let's go ahead and let's create the search functionality for that uh let's go ahead and let's close everything and first things first let's go ahead and let's install two stand so npm install two we're going to use that as our Global State Management uh to open uh the actual search command so now let's go ahead and let's create a hook for that so go inside of your hooks folder and create a new file use- search. TSX like that and go ahead and import create from tan and go ahead and write type search store to have is open which is aoan is going to have on open which is going to be a void it's going to have on close which is also going to be a void and we're also going to have a toggle which is going to be a void as well and then let's export con use search to be create with a type of search store and go ahead and open parenthesis open parenthesis again and extract the set and get options and go ahead and return an immediate object like this and press enter and let's give it a default Val of is open to false on open to be a function an arrow function which calls the set and just uses the is open to be true on close is also an arrow function which uses set to reset is open back to false and last one is going to be toggle which is an arrow function which uses set again and it's going to take the current value of this open and do the reverse of it so is open is going to be question mark get is open like this there we go uh perfect so now that we have this uh let's go ahead and let's uh install the command from shat CN UI so go back inside of your terminal here and go ahead and write mpm whoops MPX sheni at latest at command like this let's wait a second for this to install and just a couple of seconds more and and install the dialogue as well great and now let's go ahead and zoom back in and let's go inside of our Global uh components folder and create a new file called search- command. DSX and let me expand my sbar so you can see this so search- command. DSX perfect let's go ahead and Mark this as use client and let's import use effect and use state from react let's go ahead and import file from Lucid react let's go ahead and import use Query from convex SL react let's go ahead and import use router from next SL navigation and let's go ahead and let's import use user from uh add clerk cl- react great and now let's go ahead and import everything we need from our new component command we're going to have the command dialogue we're going to have the command empty the command group input command input we're going to get the command item and the command list uh great besides that we're also going to import that hook which we just created called use search from at/ hooks use- search and we're also going to import our API from convex generated API great and now let's export con search command to Be an Arrow function and let's go ahead and let's write a constant user be use user let's go ahead and let's get the router from use router let's go ahead and fetch the documents and let's write use Query and I just remembered that we actually did not write uh the function the API endpoint to load all the documents needed for search we have uh the thing to load for sidebar but for search is going to be a bit different so let's head back inside of convex inside of documents. DS right here and let's go all the way to the bottom here and let's add a new endpoint export con get search which is going to be a query it's going to take a Handler which is an asynchronous function which has the context and let's just copy and paste the identity thing as we always do and copy the user ID as well just like that and let's write constant documents to be await context. databasequery documents dowi index by user and passing the query which is just going to search for query that equals user ID to be user ID and let's filter let's get the query again and the only thing we're going to to filter is query equals query. field is archived to be false so we are not going to be able to search through our files for that we have the trash box let me just fix the trailing whitespace here uh and let's add order to be descending and let's just collect everything like that perfect and then we can just return documents that's it that's all we need for our get uh search uh endpoint now let's go back inside of the Search Command and let's use that in this query so api. documents. getet search there there we go and now let's get our toggle so cons toggle is equal use search get the store and write store. toggle let's copy and paste this two times let's change this one to be is open from the store and let's change this one to be on close from the store great and now let's go ahead uh above uh below these documents and let's add a state is mounted and set is mounted to be use State false and now let's write a use effect to change this mounted to true and go ahead and give it an empty dependency array so the reason we are doing this is to prevent hydration errors with stuff like dialogs and command uses dialogue in the background so the reason we are doing this is because uh even though this is marked as use client uh nextjs is still going to do some server side rendering on it first and with dialogues which can appear dynamically for example our Search Command can appear on a shortcut right that can cause hydration errors because in server side it's not going to exist and then it reaches uh client side and all of a sudden it exists here and that's called a hydration error so we're going to prevent that by not even allowing it to be rendered on the server side even though it already has used client that's not enough so use client still does some server side rendering right so that's why we have to use this trick that only when it's mounted we're actually going to render anything because we're going to add if uh is not mounted just return null and this way we prevent that hydration error um perfect so now let's go ahead and let's go and return our actual command here so command dialogue uh it's going to have open of is open and on open change is going to be on close go inside side and add the command input component and go ahead and give it placeholder to be open btic like this and we're going to write search open special object user question mark full name apostrophe s Jan so search Antonio's Jan in my case right uh and then go ahead and end the command list and add the command empty which is just going to render no results results found like that and then add a command group and give the command group a heading of documents and let's go ahead and let's write documents map question mark. map let's get the individual document like that or I can just immediately return like this uh and let's return a command item and first things first let's go ahead and give the command item some props so we're going to have a key which is going to be documentor ID let's go ahead and give it a value of open btic a combination of document ID Dash document.title because what we searched through is the value so we have to combine both of those uh and then let's go ahead and give it a title of document.title let's write on select for now to just be an empty Arrow function and uh we'll already added the key that's it and inside let's dynamically check for the document icon so if document has an icon in that case we're going to render a paragraph with document. icon and let's just go ahead uh and let's give this a class name of margin right 2 and text 18 pixels otherwise we're going to go ahead and render the file icon from Lucid react which we just imported and add a class name of margin right two height four and W4 great and Below all of that add a span which is just going to render document. title perfect and now let's go ahead and let's create this uh on select function right here so go above this if clause and write Con on select to have an ID of string and router. push to/ documents slid individual document and on close after that happens perfect and now let's go ahead and let's create a use effect so we have a shortcut so we can open this search dialogue whenever we want so let's do that close to this use effect that we already have here let's just prepare an empty use effect for now and let's write const down which is going to have an event of keyboard event and let's check if event. key is equal to K and if e is a meta key or r e is control key so we handle both windows and MacBooks here in that case let's prevent default and let's just toggle whether it's opened or not and lastly let's do document. add event listener D down and let's do the down function here and now let's do a return so on unmount we have to remove the event listener to prevent any overflow so document. remove event listener key down and down function and we have to add toggle inside of the dependency array and now let's copy this use on select here and let's replace it right here there we go perfect and now we have to go and add this Search Command inside of our layout but not just any layout we have to add it inside of app main layout. DSX right here and let's go ahead Above This children but still inside of Main and render the search command so we imported search command from as/ components Search Command and I think that already if you try and let me refresh if I press command key there we go you can see that I have my dialogue opened so if you don't uh make sure that you try control key and just confirm with my code that your use effect is working and as you can see it's working but you can see that the sidebar is not blurred so that's because of the zindex thing so let's go ahead and let's revisit our components UI and let's see what we have to change here so I think we actually don't have to change anything inside of the command let's see uh I think we don't yeah I think what we have to do is change it inside of dialogue . DSX so let's go ahead inside of the dialogue here and find the index 50 and change it to 59s like this go ahead and copy this and do the same thing for the content right here so we did it for the overlay and we did it for the content and I think that's it I don't think anything else needs it and if I try now there we go you can see how now even the sidebar is blurred and I can search for well everything is named the same but you can try and search something complet different to get a wrong thing and if you click on one of them you're going to get a 404 because we don't have that yet perfect so now what we have to do is enable that when we click on this that happens as well and thankfully we can do that very very easily we can just go inside of the app folder main components navigation right here and while we're here let's import use search from hooks use search like that and let's add that inside of here so const uh search search is equal use search like that and then let's go ahead and find our search item there we go it's right here and on click change this to just be search do unopen just like this and let's try that out now when I click here there we go it opens so I can use the shortcut or I can click on it right here great so we're going to do a very similar thing to enable the settings and now let's create the last item in the sidebar which is this settings right here so we're going to use a very similar method through what we did with the search right here we're going to create a hook called use settings which is going to use two to create a global store to track whether we have clicked on this or not and that's going to toggle the is open Boolean to true or false and depending on that we're going to keep that component opened or not so let's start by creating our um uh hook so let's go inside of hooks and let's go ahead and create - settings. DSX let's go ahead and let's import create from toand and let's write type settings store and very similar to search store it's going to have is open which is a Boolean is going to have onopen which is just a void and on close which is also just a void but it's not going to have a toggle and let export cons use settings here to be create pass in the settings store and go ahead and extract the Set uh well just the set function and go ahead and return an immediate object set is open to have a default value or false on open to be a function sorry not void but call a set function which is going to set the is open to true and copy and paste this and change this one to be on close and that one is going to set is open to false great and now let's go ahead and let's create our settings model so save this file close everything go inside of components models and create a new file settings- model. DSX and let's go ahead and let's mark everything as use client inside and let's go ahead and let's import everything we need from at/ components UI dialogue so we never actually installed the dialogue explicitly right but we did get it installed when we installed the command component for the Search Command right here when we install these things you can see that the command uses the dialogue right but just in case if you don't have the dialogue so if you can't find that inside of your UI folder this dialogue right here even though you should have it because I think in the previous part we changed the Z index here right but just in case you don't have it it um you can always just go into your terminal and do npx shat CNU at latest add dialogue like this I'm not going to do it because we already have it all right let me Zoom back in and let's import the dialogue the dialogue content and the dialogue header and let's go ahead and let's import use settings from hooks use settings and uh well we need a label so let's go ahead and let's do that by going inside of our terminal I I shut it down too soon and let's write npx shat cn- at latest AD Label like this wait a second for this to install and once it's done we can go back into the code here and let's import the label from at/ components slui label and lastly I want to import the mode toggle component from well it autoc completed for this but I'm going to change to S SL components so we already have mode toggle if you remember we have that inside of our uh navigation bar when we log out so let me just log out from here and there we go this is the mode total component which can switch to light and dark mode so let me just head back inside of my component here let me uh let me go back okay and now that we confirm that we have that mode toggle so you can also just contr controll click and go here to confirm that you have it and now let's go ahead and let's uh write export const settings model like that let's write con settings to be used settings and let's return a dialogue component let's go ahead and let's give this dialogue an open prop of settings is open and onop change to be settings.on close let's go ahead and render the dialogue content and let's render the dialogue header let's give it a class name of Border bottom and padding bottom of three let's add an H2 element which is just going to say my settings like that and let me just collapse that so we can safely add a class name here without taking much of my space here so the class name is going to have a large text and a semibold font uh actually let's do medium semi bold might be a bit too much uh great and now uh below this dialog header open up a div and let's give this a flex so Flex items Das Center and justify Das between and on this side uh we're going to have another div which is going to be a flex column so flex flex-all and GAP y1 and let's give it a label here which we imported above with appearance and a span which is just going to say customize how Jan looks on your device and let's give this a class name of text uh 0.8 REM and text- muted D forground all right and now let's go outside of this div right here and let's render a mode toggle just like this perfect and now if you remember in the settings uh in the so not in the settings in the Search Command where is it let me close this in the Search Command we do this trick with mounting right but it looks like I didn't do it here in the settings model why is that it's the very same logic it uses the model right and it use it to stand for the state well you're correct but we're not going to do it here because we're going to have uh another model in this application and we can do a little trick with a common component to do that for us so we can just leave the settings model as it is and instead let's go ahead inside of our providers so I'm going to close everything go inside of components providers here and create a new file model- provider. TSX mark this as use client go ahead and import use effect from react and use state from react and now let's go ahead and let's import the settings model and I'm just going to change this to be add components it doesn't really matter but I like to be consistent and now let's do expert const model provider and let's go ahead and let's return a frag whoops what did they do let's return a fragment which is going to render a list of our models the only one we have right now is the settings model so we can just do this and now here we're going to write the is mounted set is mounted we use State false by default and the use effect here let's start with an empty one and let's just write set is mounted to true so now none of the models are going to be rendered unless we are fully on the client side so on server side we're just not going to render any models that's completely fine we don't need them to be optimized or anything and that's not going to cause any hydration errors and let's just do if is not mounted return now great and now that we have the model provider we also have to add that uh model provider to our layout so let me just save this and let's go inside of our app layout. vsx and let's go ahead and add it below the toaster here so model provider and make sure you import a model Provider from here so I'm just going to keep my providers together like that so I have these three providers let me move this to the top and let me have this all the way here there we go so let me just show you you should have these three providers model providers is the one we just added here and we rendered it right here model provider perfect and now well nothing is still shown here right so if I click nothing happens so let's go back inside of our app folder main components navigation and just the same way with the add the use search go ahead and copy and paste this and rename this to use settings and change this to use settings and then you can use that here in the same way con settings use settings and go ahead and find the settings item the same way we did with the search item uh it's right here there we go so just below the search and change this to be settings. unopen on open whoops like this there we go and let's try it out now if I go into settings there we go let me see if I can click on dark mode and there we go now everything here is on dark mode and it looks like we have uh a bit of issues here so let's see first thing I'm noticing is that on dark mode this seems to have its own little border here which is just weird so we're going to have to way find a way to fix that let's see how the search looks search looks fine uh let's see how this looks this looks fine as well uh the trash looks fine but let's see if that's true when I delete something so let's go ahead oh look at this as you can see this ones look uh too white for the dark mode so let's go ahead and quickly resolve that first so have something in your trash and let's go ahead and let's revisit our trash component so in inside of app main components trash box right here let's find where that is so it's those two items right here so find the confirm model and find this div with the roll button so these two ones are our suspects and looks like we put this hover color on uh uh light mode but let's add uh dark hover in that case BG is neutral 600 like that and let's copy and paste this and add it for this div as well which has the roll button which has the trash icon so let me just add a space here and paste that let's see if that improved anything so I'm going to go back here there we go that that looks a bit better now it's not as as shiny as it was before let's just confirm yeah I think I think that looks okay it kind of looks the same when I hover here that's fine uh and let me just see what's going on uh with this right here so we have to go back inside of our item component for this let me close this let's find the app main components item and let's find the Chevron icon because that's the one uh that we need here where is this is the Chevron icon and we render it right here let's see the class names so H full rounded small hover BG neutral and then on dark oh looks like we added the BG neutral like this it's missing a dark hover like this dark colon hover colon let's try it out now and there we go now it looks a bit better perfect so this is how I wanted it to look like perfect and as you can see everything looks great here great great job so now we are ready to continue uh working here and now we're going to go ahead and work on this nov bar so when we click on this first we're going to get rid of this 404 error and we're not going to display anything yet but we're just going to be working on the kn bar so we can finally rename our documents and you're going to see how cool it is when it happens in real time using the convex database so now that we have our sidebar finished we have the working search we have the working settings let's go ahead and let's fix this 404 page which happens when we click on a specific document from our sidebar here so in order to fix that let's go ahead inside of our app folder main routes documents and in inside create another folder in uh square brackets document ID like this and inside create a new file page. DSX and let's go ahead and name this document ID page and for now let's just return document ID inside and already if you save this it should be enough to prevent any errors when I click here there we go now it says document ID so if you're wondering how this works well look at the look at the URL that I have it's Local Host 3000 documents SL the ID of my document right and let's take a look at the structure we created so we have a folder documents and then we have document ID so by using square brackets we've made this a route which can be dynamic so it can be any ID that we want it to be and that's why we can that's way this works if we remove the brackets then it would expect it to literally be document ID and it's not what we want so that's why these square brackets are important uh great so we're going to leave it like that for now and instead we're going to go ahead and focus on creating uh the navigation bar which is going to be right here and it's going to be only visible when we click on a specific um item so let's go ahead back inside of our main folder here inside of components and inside of navigation. DSX and let's go ahead here and find we already have one navigation here but this is only a placeholder while nothing is active right it's only used to collapse a specific menu item so let's go ahead now and let's add the params in here perhaps we already have them we don't so let's go ahead and write con params to be use perams and you can import use perams from next SL navigation so just make sure you add use perams from next SL navigation and now that we have prams we can go all the way down here and turn this into a Dynamic view so let's go ahead and uh make sure you're inside of this div which holds the Navar ref and then we're dynamically going to render the content so if we have rams. document ID so we're using this double exclamation points to turn this uh string into a Boolean so if we have that in that case we're going to go ahead and render a Navar component which we don't yet have which is going to have a prop is collapsed to be is collapsed which we hold in a state and on reset width it's going to be reset width like that otherwise if we don't have it we're just going to render this current nov bar which we have right here which just simply holds the menu icon to expand the menu on mobile mode and if you save of course you're going to get an error so now let's go ahead inside of this underscore components and let's create a new file navb bar. TSX and let's export const navbar and let's just return a div say navbar now we can go back inside of our navigation. TSX and we can import that from slore navbar make sure you don't accidentally import this one from the marketing page so this new one which we created which is in the same folder and let me show you uh how that looks so/ navbar make sure you have that and as you can see now I have some uh overlapping text right here because it's both the Novar and the document ID which I've written in the document ID page but let's not worry about that now and instead let's go back inside of navbar here and let's go ahead and Mark it as use client and then let's write interface navbar props to be is collapsed Boolean and on reset width to be a void and let's go ahead and just assign those props here so I'm going to extract is collaps and on reset withd and I'm going to map them to navbar props and now if you go back into navigation you should no longer have any errors regarding of the rendering of this Novar component now let's go back inside of our convex API because we need to add a specific API which we're going to use inside of this navbar function here so let's go inside of the convex folder inside of the documents. DS and let's go all the way to the bottom here and let's go ahead and write export const get by ID that's going to be a query which has the arguments of document ID which is a type of v. ID documents besides the arguments we're also going to have a Handler which is an asynchronous function which has the context and the arguments we pass in Above So as always first let's go ahead and let's get our identity so const identity is equal to weight context. out user identity and now we don't have to check for uh whether we have the Identity or not because we're going to reuse this API route for guests to view your published notes right if you remember from the demo we're going to have a functionality where you will be able to publish your notes to the web and then everyone who has a link will be able to see them even in incognito mode without them being logged in so we're not just going to check for the identity here we're just going to extract it and instead we're immediately going to fetch the document so con document is equal to await context. database. getet arguments. doent ID and first things first let's check if this even exists so if we don't have the document we can go ahead and throw new error not found and now let's check if we can already show this document to the user and the way we know that we can show this document even without checking whether we are logged or not is if the document is published so if we publish the documents that means we can already return it but I just want to do one quick thing I want to confirm that by any chance it's not archived so let's also confirm the document is not deleted from the side of the Creator and then we can safely return the document and if this is not the case in that case we continue with our checks and the first check we have to do if is if we have the identity after all if we don't have it throw new error not authenticated and now let's go ahead and let's extract the user ID const user ID to be uh identity. subject and let's go ahead and write if document. user ID is not equal it's not identical to the user ID which just extracted in that case it means we are not author ized so unauthorized to view this and otherwise return the document great so now that we have this API endpoint we can head back inside of our navbar here and let's go ahead and let's import uh use Query from convex react like that and we also have to add our params so use pams from next SL navigation and now let's go ahead and let's extract the prams and now we can do the query so cons document is equal use Query api. document oops let me show you what I'm writing uh I didn't import API so let's import API from atcore generated API right here so just as I did it here I'm just going to separate this Imports and then we can just autocomplete API documents get by ID and we have to passing the argument which is the document ID and that's going to be pam. doent ID but now we're going to have a type error here because pam. doent ID is a string but what we expect is a type of ID documents so we can just simply fix this by writing as ID and we have to import that from ATC convex _ generated data model so make sure you add this import as well and extract the ID and then we can just write ID documents and there we go no errors here perfect now let's go ahead and let's write if document is undefined for now we're just going to return a paragraph saying loading later we're going to change that to be a proper skeleton uh perfect and now let's go ahead and let's just write if by chance the document is null we're going to return null as well otherwise let's go ahead and let's change this to be a fragment like that let's add the navigation element and let's give it a class name of BG Das background let's give it a dark of BG Das hex code 1 F1 f1f PX3 py 2 w- full Flex items D Center and GAP dx-4 great so now that we have that let's go inside of here and let's dynamically render our is collapsed in that case go ahead and render the menu icon from Lucid react so make sure you add the menu icon from Lucid react I'm going to move it with this Global Imports at the top and let's go ahead and let's give this one uh a roll of button let's give it an on click on on reset width and let's give it a class name of h-6 w-6 and text- muted D foreground uh great and now out inside this uh Dynamic uh conditional rendering open up a div with a class name uh which is going to be Flex items D Center justify Das between n w- full like that and let's just write Novar here and there we go let's take a look at how this looks now as you can see on desktop it just says Novar and as you can see right now uh it looks like our document ID text has disappeared but it's still here is just that we've given this a white background so it's no longer visible but don't worry when we come back to the document we're going to properly align all that and as you can see when I completely collapse this I have a nice little menu here and if I go ahead and delete this you can see how it still says navbar here so let's see if that's oh okay because the URL is still here so let's manually just change to SL documents like this there we go and if I Collapse now you can see how we still have all of that and if we go here then you can see how it's next to the nov bar here because it's going to load the that specific document here perfect uh so make sure you click on a specific document make sure you have the N bar here and what we're going to do now is we're going to create a little component called title so the user can actually change the title from here so inside of this nav element right here let's go ahead and replace this text which says navbar with a title component which is going to have initial data to be document which we fetch from this query right here and if you save of course you're going to get an error because title does not exist as a component yet so let's go ahead and quickly create that right here in the underscore components where our Navar and navigation is create a title. vsx let's go ahead and Mark this as use client as well and let's export cons title and return a div saying title now we can go back to the nov bar and we can import that uh make sure you don't import from any of this so just do /title it's in the same folder so just like that no need to complicate it further now let's go back inside of the title component and let's give it some props so interface title props initial data is a type of document which we get from at/ convex uh generated data model and let's go ahead and extract documents like that and let's go ahead and extract that here so title props and initial data uh great so now what we have to do is create another API endpoint which is going to be used uh for updating a document since we're going to change the title here we have to prepare that endpoint so let's go back inside of our documents in the convex folder right here let's go all the way down and let's go ahead and write export const update to the am mutation which has uh the following arguments it will have an ID which is a type of ID documents it's going to have the title which is an optional value so not always are we going to have to pass that and it's a type of v. string we're also going to have the content which is also optional and also a type of string we're going to have a cover image which is also going to be optional and the type of string besides the the cover image we're also going to have an icon which is also an optional string and last one is going to be is published which is an optional Boolean great and now let's get our Handler which is an asynchronous function which has the context and the arguments and let's go ahead and let's start by checking if we have the identity so const identity is equal to await context. r arguments sorry context. out.get user identity if there is no identity in that case throw new error un unauthenticated okay and now let's extract the user ID to be identity. subject and now let's prepare our data from these arguments right here so I'm going to go ahead and write const from the arguments and I'm going to extract the ID and then I'm just going to keep everything as rest so I'm D structuring everything inside of these arguments and that's all of this stuff and I'm purposely extracting the ID because we're never going to send the ID to be updated we're just going to use it um to find what we have to update but with this one we can just pass in the rest in the patch function and that's going to update whatever we send it so if we sent a title title is going to get updated if we sent a content content and same for all the other things so we kind of eliminated or omited the ID from the arguments object by creating a destructuring method here where we just kept everything else inside of a constant name rest and extracted ID separately all right and now let's fetch the documents so con existing document is going to be await context. database. getet arguments ID and now let's go if there is no existing document throw new error not found and let's check if we have the authorization to do this so if the existing document. user ID is not identical to the user ID we extracted using the identity subject in that case throw new error unauthorized and now we can just do cons document to be await context. database. patch arguments. ID and just spread rest and return the document just like that we have our uh Pat function here perfect so we're going to use this uh function update every time we need to update something and the first place we're going to use it is inside of the title component so let's go ahead and let's import use mutation from convex react and let's also import our API from convex generated API and let's go ahead and let's write const update to be use mutation api. documents Dot and it's right here update great so now that we have that let's go ahead and let's actually render something here so in this div we're going to go ahead and give it a class name of flex items D Center and GAP X1 we're going to go ahead and render the icon if we have it so if inside of the initial data we have the icon in that case go ahead and render a paragraph oops my apologies and inside we're just going to render the initial data do icon that simple and now let's go ahead and write uh well first we have to add a state called is editing so let's go ahead and do that const is editing and set is editing it's going to be used state from react with a default value of false so let me just move this to the top right here uh great and now let's go ahead and let's dynamically render using this is editing Boolean right here so if we are editing then we're going to render the input component from add/ components UI input make sure you have this imported from chaten like this and let's go ahead and let's give it uh well for now we can just give it a class name of h-7 px-2 and focus D visible to be ring- transparent like that uh and we don't have to give it anything else for now and let's go ahead and write the else function here instead we're going to render a button like that so make sure you import button from at/ components UI button and what we're going to do is going to render the initial data do title like this inside and let's go ahead and give this button and on click which is just going to be an empty Arrow function for now a variant of ghost a size of small and the class name of font dormal h- aouto and padding of one and let's wrap this inside of a span and let's write a class name truncate great so now if you take a look here this becomes a little button and when we click on it we're going to change this is editing state to true and then that's going to turn into an input so let's go ahead and play around with that so let's add some refs that we need here and some functions so I'm going to write const input ref here to be use ref from uh react make sure you import user ref right here and let's give it a type of HTML input element with a default value of null great now let's go ahead uh and let's create uh the title uh uh state so cons title set title is use State and the default value is initial data. tile or Untitled just in case we are not able to load it and let's write Con in enable input to be set title to initial data. title in case it has changed in that time and then set is editing to be true and set timeout with a timeout of zero so we're just using a little hack here because I want to focus on the input and to do that we're going to use the input ref. current question mark focus and we're going to input ref. current question mark set selection range to start from zero and end by the entire length of the value great and now that we have that uh let's go ahead uh and let's add a couple of more functions which we need here so we're going to need the disable input con disable input is going to be a function which just calls Set uh is editing back to false so very simple but we're going to use it in a function so it's easier to call and now let's do const on change to be a type of event which has the react. change event HTML input element like that and let's set title locally to be event. target. value and then let's go ahead and let's call our update function our update mutation to have the ID of initial data doore ID and let's pass in the title to be event. target. value or Untitled so if we try to delete everything it's automatically going to go back to Untitled we're never going to allow uh the user to delete the entire title if they remove everything it's going to be Untitled all right and now that we have this let's just create a uh Onkey down function so Con on key down and in here we're going to check if the user presses enter we're going to consider that they want to save that so go ahead and get the event again which is react. keyboard event with a type of HTML input element and we're going to check if event. key is enter in that case disable the input great so now that we have all of that let's go ahead and let's go revisit our input here let's give it a ref of input ref let's give it an on click to be enable input let's give it an on blur to be disable input let's give it an on change to be on change on key down to be on key down and the value is going to be our State title great and now let's go ahead and give this button and on click to be enable input like that and that should be it let's go ahead and let's see if this is working so right now this file is called entitled let's confirm that in the dashboard here uh okay let's see it's the this one I think let me actually just clear everything so it's easier clear the entire table from convex here great now we have an error don't worry we're going to solve that so for now just go ahead to slash documents so remove the ID from your URL and let let's create a new page here let's go inside of it and let's go ahead and let's rename and there we go it's renamed in real time because we're using a realtime database so our use Query which uses the get sidebar endpoint is also watching for the changes in real time and we can do that for the children as well so we can change this and you can see how it reflects even though it's a children so it's a very very cool database perfect you can see how this is changed in the database as well if you have any doubts perfect so now what I want to do is I want to add a skeleton for this title so we can actually use some proper loading state in the nav bar so let's write title do skeleton uh skeleton to be function title skeleton and go ahead and return the actual skeleton which we have from at/ components skeleton so just make sure you import that and let's go ahead and give this one a class name of h-9 w-16 and rounded medium perfect now let's head back uh inside of our navbar component find where we have this if document is undefined we just return a paragraph and go ahead and change that instead we're going to render a NV bar which is going to have uh the very same uh class name here except for the flex so we don't need Flex you can just copy BG background dark PX3 py and you can just copy this actually we do need Flex as well uh so let's go ahead and copy the flex as well my apologies so just like this BG background or dark background padding uh on siid three padding on top and bottom two uh W full like this and the flex and items Center great and inside what we're going to do is render title do skeleton like this and now if you try and play around with this you can see how for a second we have a nice skeleton instead of just a blank page here you can see how it's a skeleton here perfect and you can play around you can reduce the size of the skeleton if you think it's too much so let's see let's use height six for example maybe that's going to look a tiny bit better let's yeah maybe maybe not but you know you can play around and find the one you need so let's see if I can reduce it to maybe three and maybe increase this will that do any better let's see uh maybe I go back to four so I'm just trying it out now uh let's see if that looks better yeah looks like that one uh looks the closest maybe uh five would that be better let let me just bring it back to six yeah okay I'm I'm going to bring it back to what was it before was it nine it doesn't really matter you can play around but I just want it to look as good as possible okay let's leave it like this uh great great job so you started with the navbar and we do have a couple of more things to add to the Navar we're going to do that later um but what I want to do next is a little Banner which is going to show if you deleted a specific uh document so if you go ahead and click it from here you can still work with it right but I want to show a little Banner here which is saying that this is archived and give you the actions to unarchive it so now let's go ahead and create that little Banner here so let's go back inside of the let me just check the navigation a bit I think I've noticed while I was watching the video back I think I've noticed I made a a mistake in in one of the V uh this W full classes perhaps it's here in the knv bar let's see W full here looks good W full here looks good oh there we go I have a little bug here so this should be W full all right uh great so now let's go ahead and let's create that Banner component so that's why we added a little fragment here so we're going to go below this right here and we're going to write if the document is archived in that case we're going to go ahead and render uh well let's not use the question mark let's use the end end option in that case we're going to render a banner component and you can go ahead and save this and don't worry about the error for now and let's go ahead and give it a prop of document ID to be document uncore ID because that's the only thing uh that we need from it and now let's go ahead inside of the components and create a new file banner. DSX like that let's mark this as use client and let's go ahead and Export con banner and return a div saying banner and go back to navbar right here and import the banner from slash uh Banner like that and now let's go ahead and give it an interface so I can already see the banner here if you can see it it's probably because you're looking in one of these so make sure you're in the trash or just add something to the trash and then click on it here and then you should see this Banner because we are only rendering the banner if the document is archived great so now let's go inside of the banner and let's go ahead and create an interface so interface Banner props it's going to accept a document ID which is going to be a type of string like that uh you can just leave it uh actually you can let's give it the proper type this is the proper type yeah like that and now let's go ahead and extract the document ID and let's assign that to Banner props and I just have a typo here so ID like that and there we go looks like this is okay because underscore ID is this type right here ID documents perfect so we can uh give it that type in the props great so now let's add our router so con rout is equal to use router from next SL navigation so make sure you add that I'm just going to keep this import separated besides the router we're also going to have our mutations for removing and restoring so const remove is going to be use mutation from convex react make sure you add this so we're going to have two mutations this one is going to be API so make sure you add API from convex generated API api. documents. remove and copy and paste this and this one is going to be Resto store and the API route is going to be restore like that uh great so now let's go ahead and let's import toast from the soner package and let's go ahead and create some uh functions here so const on remove it's very simply going to be a function which gets the promise and calls the remove mutation and passes in the ID to be document ID which we have in the props and then we can do toast. promise passing the promise and do the loading state which says deleting note we're going to have the success state which says note deleted and we're going to have the error state which says failed to delete note perfect and now we can copy and paste this and it's going to be very similar for the restore option so this one is going to be called on restore and instead of uh prom is being removed it's going to be restore and instead of deleting it's going to be restoring instead of note deleted it's going to be note restored and this is going to be failed to restore note great so now we have these two actions on remove and on restore which call these two mutations here and now let's go ahead uh and yeah once let's also go back to this on remove here and once we remove it uh let's also add a redirect so do then here let's just go ahead and write router. push to/ documents like that great uh and now let's go and finally style this Banner here so go ahead and give this div a class name of w-o background of r-500 great text is going to be centered text is also going to be small padding is going to be two text is going to be white Flex items Cent Center Gap X2 and justify Center great and now let's go ahead and give this a paragraph which is going to say this page is in the trash like that and now go ahead and add a button here and let's import the button from add/ components UI button so make sure you have this import here great and inside let's just say restore page and now let's go ahead and give it a size of small let's give it an onclick to be on restore which is the function we created a variant is going to be outline and let's go ahead and give it a class name of Border D white BG transparent cover dg- primary sl5 text is going to be white let me just scroll down so this is Centered for you on Hover text is also going to be white padding one px2 height is going to be Auto and font is going to be normal there we go and then you can copy and paste this button like this and make this one to be delete forever forever like this and use the function on remove like that but we're not going to do uh just that I also want to import the confirm model so let's go ahead and import confirm model and remember this one doesn't use the two stand instead it has a trigger and Trigger is whatever we wrap it around so in this case it's going to be this button so confirm model like this and just wrap the entire delete forever button because this is an irreversible action so we want to make sure that user has the confirmation for it if they accidentally click on it and then you can remove this on click from here from the button and instead you can add it here and write on confirm to be on remove great and let's go ahead and try this out so now it says this page is in trash if I click restore it says note restored perfect and if I go ahead and delete it again now it says this page is in trash and if I go ahead and check it's right here if I click delete forever I have a model and confirm and uh okay looks like it did not redirect us back let's confirm why that is happening that we do something wrong here all right it we do remove then we do a then H let me just see what's going on here so let's try not by wrapping it in a DOT then let's just do it at the end of the function and remove this chain to then here uh now we're still going to get an error because we are here so just go back to SL documents like we did previously and let's go ahead and attempt this again so let me delete this and let's go ahead and delete this forever and there we go now we don't have the error great so just go ahead and add that uh to the end of the function no need to wait for this promise so we actually want to be faster than the promise uh great so now you have a fully working Banner which shows Once you delete a specific um a page and just one thing that I want to do before we go on to some other things that we're going to have in the nav bar uh is I want to visit our uh components UI button. DSX and as you can see it says that rounded is medium I want to change it to rounded small like that so I want every single button in in our page to have a smaller roundness and I just think it looks closer to original notion uh great so we did that and and let me just go ahead and reset everything so the next thing we're going to do is we're going to add a little uh menu here to also be able to delete the entire page from here so currently we can only do it from this three buttons it would be nice to also have those three buttons here at the top so let's do that let's go back inside of our Navar component so inside the app folder main components Novar and besides the title right here go ahead and add a div right here and give it a class name of flex items Das Center and GAP X2 and go ahead and render the menu component which we don't yet have and give it a document ID to be documentor ID and save the file and of course we get a little error here so let's go ahead and create that comp component menu. CSX mark it as use client and let's export const menu and return a div saying menu and now we can go back inside of the enough bar and fix this error so make sure it's not the Lucid react import but the do/ menu so from this very same folder the same thing we did with the title and the banner and now obviously we have some typescript errors here so let's go ahead uh and uh let's give it an interface menu props and I'm just going to write the document ID to be a type of ID documents great and let's go ahead and assign those props here so menu props and let's extract the document whoa ID great so I just want to bring your attention this is called menu and this from Lucid react I named menu icon so if you didn't do that if you have any errors with conflicting menu That's because menu icon from Lucid react can also be imported as menu but as you can see then we have duplicate identifier here so make sure that the one from Lucid react is menu icon or you can do menu as menu icon for example just want to bring your attention if you're wondering if you for example didn't do the icon thing and instead just named the menu uh great so we have that now and now we can head back inside of the menu here and let's import everything we need from the drop-down uh menu so go ahead and import from s/ components UI uh and drop- down menu right here we're going to need the drop down menu itself like we always do and we're also going to need a trigger we're also going to need the content we're going to need the individual item and a separator uh great and now let's go ahead and let's add some Hooks and some functions here so const router whoops const router is use router from next SL navigation make sure you have that UT I'm just going to move it all the way to the top because I like to separate my Global inputs from the ones starting with uh my alas besides the router we're also going to extract the user from us user which we GL from clerk as I did right here and you already know that I'm going to move it here to the top all right and now let's go ahead and let's also get the archive mutation so use mutation from convex react it's right here let's get it out to the top and we also need our API so make sure you import API from at convex generated API and you already know API documents. archive like that and let's write const on archive to Be an Arrow function where we we are simply going to get the promise to be archive with an ID of document idid and let's write toast from soner so just make sure you add this soner import so toast from sunar is going to be a promise passing that promise and let's give it a state of loading to be moving to trash let's give it a success state of uh note moved to trash and an error State uh to be piled to Archive note great and let's also do router. push to/ documents here once we archive it uh great and now instead of this div let's render the drop- down menu which we imported let's add a drop- down menu trigger let's give it a prop as child and inside let's add a button component so make sure you import the button from shaten UI I mean from components UI all right and let's give it a closing tag and let's just simply write more horizontal from Lucid react so we're going to render an icon uh from Lucid react and now let's style this a little bit so give this button a size of small and a variant of uh ghost and give this one a class name of H4 n W-4 so we uh make this a bit smaller now add the dropdown menu content and inside of here let's go ahead and give it a class name of w- 60 let's give it an Aline on end a line offset of eight and force amount so let me just collapse all of this like this so it's easier for you to see great and now inside of here we're going to go ahead and render the drop- down menu item and we're going to render the trash icon from Lucid react so make sure you import uh trash okay we're going to give this trash a class name of h-4 W-4 and margin right of two and below that we're going to add a delete text and give this drop down menu item on click to be on archive uh great and now below this drop down menu item add a drop- down menu separator which is going to have a div which is going to say last edited by user question mark full name like this and give this D A Class name of text extra small text muted foreground and padding off to perfect and now let's go ahead and see how that looks here so now I have these three buttons here and there we go it says delete I can press on it and as you can see it's moved to trash and I can visit it back from here perfect so now let's go ahead and let's turn this into a skeleton as well so when this is loading the same way we have a skeleton here I want to have a skeleton on this side as well so in order to do that we're going to go inside of this menu component and add menu. skeleton to be a function menu skeleton and go ahead and return a skeleton component which I imported right here from components UI skeleton and go ahead and give it a class name of height 10 and W10 let's go ahead back inside of our Navar now and we have to find uh this loading state which we have and create a div below it with a class name of flex items D Center and GAP X2 and add the menu do skeleton inside like that we are also going to have another element inside of here so that's why we have this Gap X2 the same reason why we have it here because we're going to have two elements here but we're going to skip that one uh for later and now if you refresh everything you should see for a second a little skeleton there as well so let's just try it out oh oh no we are not seeing it it seems to be right here so let me see uh what we did wrong I think I know what we did wrong so let's just quickly uh see our loading State here oh all right so I think it's because in this Novar when we give it a flex we don't give it a justify between option so let's give it justify Dash between so this title skeleton and this div is going to be far away from each other so let's try that now I'm going to refresh everything again and there we go now for a second there I saw a nice little skeleton perfect so I have the skeleton here I have it here exactly what we wanted great so now we're finally ready uh and it seems like I have some weird error here let me just reload my window uh okay and now we're finally ready to start developing uh the well the view for this document ID page we're going to start by adding uh some title here and then a cover image and then the editor itself and then what's left is to add a publish button great great job so far so now that we have our knv bar semifinished here let's go ahead and let's head back inside of the document ID page so we can start rendering some content here so find the document ID page it's located inside the app fold main routes documents document ID page. TSX right here and first thing I want to do is I want to mark this page as use client so use client and let's go ahead and let's import the API and the use Query so use Query from convex react and let's import the API from convex generated API and then let's go ahead and let's write an interface document ID page props which is going to have prams which have the document ID inside of it which is a type of string but for ease of use we're going to write ID which we can import from convex generated data model and we're going to write documents so we don't have to change that every time we use it here and how do we know that we have this params and document ID where does the this come from well it comes from this this folder name since this is a dynamic folder and because you can think of this as a variable so we created this folder which becames a part of the URL and the route and whatever is written inside of parentheses becames a variable which we can access through params props so every route has this page has this params if it's visible and there are different ways you can access perams so you can do it directly in the page like this if it's that specific page or you can use the use params which we do in document list for example you can see that we use do params here and you can see that I have pam. doent ID right here so it's the same thing but this way we don't have to use that hook it would work if we use the hook as well but I just want to show you different ways of doing that great so let's go ahead and extract that uh params here and let's go ahead and give it the type of document ID page props um great and now let's go ahead and let's fetch the document using the query so con document is equal to use Query API the documents. getet by ID and let's go ahead oops comma and let's go ahead and give you the argument of ID oh sorry document ID to be pam. document ID great and now let's go ahead and let's just write if the document is undefined in that case let's just go ahead and write a div loading later we're going to change it to a proper skeleton and let's go ahead and let's write if document is null let's go ahead and let's return a div saying not found and I forgot uh the return here my apologies so wrap this in a return great and now let's go ahead and write the main function here so let's go ahead and give this div a class name of padding bottom 40 like that and let's go ahead and create a div here with a class name of MD Max 3 EXL so we are limiting how wide this document can go and on large devices let's do MD uh Max W4 Excel and let's write MX outo actually this is a mistake sorry so LG is Max W4 Exel great great and now inside of here let's go ahead and write document document ID like that and it seems like we're still not seeing it because it's behind the sidebar but that's okay for now uh we can ignore that uh instead let's go ahead and let's replace this document ID with a toolbar like this a toolbar doesn't exist yet and all it's going to have is initial data to be our document which we load so now let's go ahead and let let's create this toolbar component so we're going to reuse this toolbar component so we're going to create it inside of the components folder so create a new file toolbar. TSX let's go ahead and Mark it as use client and let's export cons toolbar and return a div saying toolbar and go back to page. CSX and then you can import toolbar from s/ components toolbar like I did right here great now let's go ahead uh and let's give it some props so go ahead and write interface toolbar props and we're going to have the initial data which is a type of doc from convex generated model specifically the documents and we're also going to have another prop which is going to be optional and used later called preview so it's going to be an optional Boolean and let's go ahead now and let's just assign those so toolbar props and let's extract the initial data and the preview great and now what I want to do is I want to go back inside of our page uh. DSX right here and let's go ahead and let's just add a div here it doesn't really matter let's just give it a self-closing tag and give it a class name of h35 VH like this so it gives a lot of space right here perfect and now let's go ahead inside of this toolbar and let's go ahead and style it a bit so I'm going to give this toolbar a class name of padding left 54 pixels group and relative like that and now let's go ahead uh and let's check if we have the icon so if we have the icon initial data icon and if this is not a preview meaning that we are looking to edit this right we are looking uh at this as an owner not as a guest who is looking at our published document if that is the case let's go ahead and render a div like this with a class name of flex items D Center Gap X2 like that group slash icon and padding top of six like that perfect now let's go ahead and let's install a package uh called let me just expand my screen so let's install a package called Emoji picker react go ahead and install that so let's wait a second for this to finish great and let's go ahead and let's create a new component called icon picker so let's go inside of our components folder and create a new file icon dasp pier. DSX and let's just save our work in the toolbar here and let's go ahead and Mark this as use client let's go ahead and import Emoji picker and let's go ahead and import theme from emoji picker react let's go ahead and import used theme from next themes which we already have installed and let's import everything we need from components UI popover and that's going to be the popover itself the popover content and the popover trigger great let's create an interface icon picker props which is going to have an onchange which takes in the icon which is a type of string because it's just going to be an emoji children are going to be react. react node and as child is going to be an optional Boolean great now let's go ahead and assign that so expert con icon picker and let's assign the icon picker props let's extract all of those props so onchange children and as child and for what I want to do now is get the resolve theme from used theme and let's go ahead and write const current theme the check for the resolved theme or give it default value of light as key off type off and what we have to do now is write theme map and let's go ahead and create theme map so cons theme map is an object which has an option for dark which is going to use theme. dark and it has an option for light which is them. light so I'm doing this because this Emoji picker from emoji picker react has its own theme prop so we have to use the theme that we are using using next themes and then map it correctly to what is compatible with their theme and then let's just write cons theme to be theme map current theme all right and now we can finally do return get the popover write the popover trigger give it an option as child to be as child and inside render the children so that's going to be our trigger and then write popover content let's go ahead and end this like that let's give this popover content a class name of padding zero W full border none and Shadow none and inside we're going to render the Emoji picker which is a self closing tag let's go ahead and give it a height of 350 let's give it a theme of theme and let's give it an on emoji click to get the data and use the on change to send data. emoji which is a string like that perfect so now we can head back inside of the toolbar right here and now we can add that icon picker here so import the icon picker from do/ Pier because both of those are in the global components folder so we working in toolbar and we just created the icon picker here and go ahead and pass inside of the icon picker uh a paragraph which is going to be the initial data. icon great and now let's go ahead and give this a class name of text 6xl cover opacity -75 and transition and give this icon picker an unchange to just be an empty Arrow function for now like that and right now you should not be able to see anything here but let me show you a little trick to how you can uh preview something here so not big deal if you don't do this you can just continue coding as normal but I want to demonstrate that code picker to you so right now that emoji picker is only going to be shown if we already have an icon so let's go ahead and see that we can very easily do that by going inside of our database here go ahead let me just clear everything so I'm going to go uh and delete this uh and let me just delete this one as well like that so let me go to this page and let me clear everything I have inside let's create a new one and now all I have to do is give this document uh if I can do that from here I'm actually not uh sure you know what ignore that let's just continue developing I think I just confused you even more okay I wanted to show you already but we're just going to have to be patient I'm sorry for that uh all right so now let's go ahead outside of this uh conditional right here actually uh go outside of this icon picker here and add a button component so import uh this from UI button but I'm going to change it to components and go ahead and add a button and render the X icon from Lucid react so make sure you add this import here so this is going to be our button to remove the icon if we have it and add a class name of H4 n W-4 and let's go ahead and give this an on click for now to be an empty Arrow function and let's give it a class name of rounded Das full opacity -0 group whoops group- hover SL icon opacity 100 transition and text- needed uh Das foreground and we also need to give it a text of extra small besides class name give it a variant of outline and a size of Icon great so I'm sorry you can't already see this perhaps I should have approached this in a different way but it's quite hard to do it already so let's just continue the way we are I know you can't see this but when we add an icon you're going to be able to see this so this is going to control uh the icon we can change or we can remove it Al together uh great so now let's go ahead uh below that and let's write otherwise if we have the initial data that icon but if this is a preview meaning that a guest is looking at this in that case it's going to be much simpler we're just going to render that icon so add a paragraph and render the initial data do icon inside and let's give this paragraph a class name of text- 6 Excel hover well it doesn't need any hover it just needs a padding top of six so this is going to be if a guest is looking at it so this is if we are looking at it that means that we can change the icon and we can remove it right but a guest can just look at it uh great so now let's go outside of this and let's create a div here with a class name of opacity d0 group - hover opacity D100 Flex items D Center Gap dx-1 and padding y off four and inside if we don't have the icon and if this is not a preview in that case go ahead and render the icon picker again and this time we're going to add a button component with a smile icon inside so import smile from Lucid react the same way we did with the X icon let's go back down here all right and let's go ahead and write add icon and give this smile a class name of uh it's going to be H4 W-4 and margin right to give this button a class name of text- muted D foreground and text extra small give it a variant of outline and a size of small and give this icon picker as child option and on change it's just going to be an empty Arrow function perfect so now if you find it so just go ahead and hover over your screen or you can remove the opacity like this so just change the opacity to 100 there we go and now you can click on this and there we go we have our emojis here so it's going to be right here the reason there's so much space here is because I just wanted to move it away from the knv bar later on this is going to have a cover image so that's why we're not going to need all that space But for now you can look at this uh icon how it looks here and if you change the appearance to dark this is going to be dark as well so that's what I wanted to do great so now we have this add icon element here so let's quickly change this default opacity if you change it back to zero perfect so now that we have that uh let's go ahead uh outside of this so still inside of this div but outside of this conditional render here and go ahead and write if we don't have initial data cover image and if this is not a preview then go ahead and render a button again which is just going to render the image icon from Lucid react so make sure you import the image icon give this a class name of h-4 W-4 and margin right of two and give this button a class name of text- muted D foreground text- extra small give it a variant of outline and size of small and for now give it an on click just an empty Arrow function like that and if you try again and hover again uh we now have two buttons here but we forgot to add a little text here below this image icon to be add hover perfect so now you can see when you just try and hover somewhere and there we go you going to have add an icon and add a cover and if you're wondering why are they only showing when I hover in something well that's because we don't have the title rendered here but we're going to have that soon and it's going to make a bit more sense all right so now that we have that let's go ahead and let's add some states here let's go ahead and add some functions here so first thing I'm going to add is const input ref to be use ref from react and give it a type of element ref which has a type of text area with a default value of null then let's go ahead and let's add uh the state is editing and set is editing to be use State and false and make sure you import use State from react so I have uh element ref use ref and use state from react and let me just move that to the top all right besides that we're also going to have the value and not import sorry constant value and set value from use State and default is going to be initial data. title then let's go ahead and let's write const update to be use mutation from convex react so make sure you add use mutation from convex react and and we're going to call the API so we have to import that as well which I just did right here so import API from convex generated API I'll just move it with the other stuff here and we're going to use api. documents. update so we already created that function uh great and now let's go ahead and let's write const enable input to be an M function if it's preview just break the function right because a guest is looking at this so a guest should not be able to even click on this but just in case they do uh we're just going to break the function let's do set is editing to be true and let's do set timeout to set the value of initial data. title and to do input ref. current Focus like that and zero as the timeout value uh great now let's go ahead and let's add add con disable input to just very simply be set is editing to false now let's go ahead and let's add const on input to take the value which is a string and do set value value and update ID initial dataor ID and let's give it a title of value or un titled if we try and delete the entire title so very similar to what we did in with our uh navbar uh great and let's add one more thing const on key down to take the event which is a type of react. keyboard event and it's going to come from html text area element all right and inside of it what we're going to do is check if event. key is enter and if that's the case we're going to prevent default and we're going to disable the input um great so now we can go back uh inside of our code here go all the way down where this ends and go outside of this div so right here we should only have one closing div at the bottom and show if is editing and if it is not preview in that case go ahead and render uh a component which we actually have to install which is called uh text area Auto size so let's go inside of our terminal and let's install mpm install react text area Auto size like that so that's going to help us because uh well we could code our own but this is just a faster way of doing it so let's go ahead and import that here so import text area Auto uh size from react text area Auto size perfect and let's go ahead and use it here so if we are editing and if it's not a preview we're going to render that text area out to size and give it a ref of input ref go ahead and give it an on blur to be disabled input go ahead and use the on key down to be on key down go ahead and give it a value of value on change is going to take the event and call the on input with event. target. value and let's give it a class name of text- 5 Excel BG - transparent font uh trans arent font Das bold break dwords outline Das non text Dash open pointy square brackets 3 f3f 3F and on dark mode text is going to be cfcfcf and let's add resize Das none so we don't have that icon uh and now let's go ahead and write the else here this is going to be the last element here uh and let's go ahead and write a div which is going to render the initial data. title and let's go ahead and give this an onclick of enable input and let's give it a class name of padding bottom padding bottom to be 11.5 pixels text five pixels five Excel sorry font dbol break words outline dnone and we can copy this text and dark text from the text area out of size and just whoa I copied too much so just this and paste it here like that and that should be it let's go ahead and take a look now so there we go we now you can see now it makes sense when we hover on our title that's when we have the options to add an icon or to add a cover and let's try and click on this and then you should be able to modify this end because we use the update function is here in real time so now if you change it from the nav bar it's right here or from here it's visible in all three places amazing amazing job uh so now I want to go ahead and enable us to add an icon because currently when we do that nothing happens in order to do that we have to create an AP a function for it so we don't actually need an API route to add an icon but we do need it to delete an icon so let's go inside of convex inside of documents right here let's go all the way to the bottom and let's add export cons remove icon to be a type of imitation let's get the arguments to be an ID of we. ID documents let's get the Handler to be an asynchronous function which has the context and the arguments and let's do the thing that we always do which is get the identity and the user ID so I just copyed that from the function Above So const identity we check if we don't have the identity and extract the user ID now let's get the existing document using await context. database. getet uh sorry context. datab database .g rs. ID like that if there is no existing document in that case you can just throw new error not found and let's also do if existing document. user ID is not identical to the user ID which we just extracted in that case throw new error unauthorized uh and now let's go ahead and write cons document to be await context. database. patch arguments. ID and let's write icon undefined so we remove that row and write return document here perfect so now we can go back inside of the toolbar right here and let's write two functions here so we're going to write const on icon select to get the icon which is a string and call the update function using ID to be initial dataor ID and an icon like this so very simple and now let's go ahead and do const on remove icon to be remove icon oops remove icon and we don't have it so let's go ahead and create it here so const remove icon is use mutation api. documents. remove icon all right so we called the remove icon and we're just going to go ahead and pass in the ID to be initial data ID as simple as that and now we can assign those two so find the icon picker here and go ahead and give it an option of on icon select for this button which holds the X class the X icon sorry uh give it an on icon remove or on remove icon sorry and we need the on icon select inside of this icon picker below as well like this and let's try it out now so if I click add an icon and select an icon there we go it's in all three places it's right here it's in the sidebar and it's right here and I can just as easily remove it or bring it back and it's also in my database perfect so we finished uh this heading here and now we're going to go ahead and create our cover and upload our image great great job all right so now that we have this toolbar and the icon working let's go ahead and let's enable this add cover button right here so before we go on to implementing the actual Edge store which we're going to use the upload the files first let's go ahead and create a model which is going to open when we click on ADD cover for that let's revisit our Hooks and let's create a new file use- cover- image. DSX and let's go ahead and let's import create from tand let's write the type cover Image store let's give it is open to be a Boolean onop to be a void function on close to be the very same void function and then let's do export con use cover image to be create and let's give this create a type of cover Image store and let's extract set so just do double parenthesis inside set and open an immediate object like this wrapped in parenthesis and now let's assign is open to be false by default on open to call a set function which sets the is open to true and on close we'll call the set as well and set is open to be false great and now let's go ahead and let's create our cover image model so go inside of components models and create cover image model. TSX let's mark it as use client and let's import everything we need from s/ components UI dialogue here so we're going to need the dialogue itself whoops dialogue we're going to need the dialogue content and the dialogue header let's also import the use cover image from the hook we just created and now let's write export con cover image model to very simply use the cover image from use cover image and let's go ahead and let's return our dialogue so dialogue and let's give it a prop open to be cover image on open sorry is open and let's give it an onop change to be cover image on close inside of the dialogue add the dialogue content add the dialog header uh let's see did I import the dialog header I did okay add the dialog header and add an H2 class sorry H2 element which is going to say cover image and give this H2 a class name of text- Center text- large and font semi bolt and outside of the dialog header let's just do a div which says to do upload image great and now let's go ahead and let's add the this entire dialogue inside of our models provider so let's go inside of providers model provider here and let's go ahead and import cover image model and I'm just going to replace this to add/ components cover image model so models cover image model which we just created and let's add it below right here like that and then we can go back inside of our toolbar component so that's inside of components if I'm not mistaken yes it's right here and let's go ahead and let's import it here so I'm just going to do it here const cover image to be use cover image from hooks use cover image so that hook we just created I imported it right here all right and now that we have the cover image let's go ahead and find the button which adds our cover image so I believe it should be right here this button which currently has an empty on click which says add cover and has the image icon so let's go ahead and give it a on click cover image on open and now if you go ahead and click on ADD cover there we go you can see how we have a model and we have a to-do upload image great so this is a great start now we have our model which is going to open to upload a new image uh what we have to do now is set up Edge store so in order to set up Edge store we have to visit EDG st.dev or you can use my link in the description so let's go ahead and click Start for free right here and you can create an account using GitHub so let's go ahead and just sign in and as you can see right here I already have a project which I used for the initial development of the notion tutorial but for you go ahead and find the button which says new project and let's go ahead and name this notion Das tututorial and let's click create let's wait a second for this to install and there we go now you should be seeing these two keys right here uh if this changes in the future don't worry I'm going to also show you some alternative ways where you can find your keys uh inside of your project but if you are seeing this screen you can just go ahead and copy this keys and go inside of the environment file which we have so let me just close everything here find your environment. local right here and just go ahead and paste the Keys here so you need to have Edge store access key and Edge store secret key um great let me just quickly show you that you can always go inside of your projects you can select a specific project and from here you can always click on this little info icon and then click on Project keys and you can visit them here as well so later when we actually have some files inside this screen is going to disappear but you can always see it by clicking on this little info here and then clicking on Pro project Keys um great so now let's once we've added this inside of our app let's click continue Indo right here and let's install everything we need so we need to install edge store server Edge store react and Zod and let's go ahead and click copy here and let's go inside of our terminal and I'm going to go ahead and install all of that so I'm installing Edge store server EDG store react and Zod and let me just zoom out one more time just so you can see all this in one line so EDG store SLS server Edge store SL react and Zod and just go ahead and press enter great uh once this is installed let's go ahead and see what the next steps are so we need to add the environment variables but we already did that and now let's set up our back end as you can see it has both the instructions for the app folder and for the pages folder we are working with the app folder so this pre-selected option works for us and you can just go ahead and click copy from here if for any reason you cannot find this you can always visit my source code and go ahead and find the file that I'm about to create now and we can take a look at where we need to create it so we need to create it inside the app folder API Edge store create the dynamic Edge store Route and then route. DS inside so let's go inside of the app folder create a new folder called API and inside create another folder called Edge store like this and then inside create another folder in square brackets and spread Edge store inside like this and create route. DS and just again copy everything from this file or copy it from my GitHub and you can just go ahead and save this file um great now that we have this let's go ahead and let's see what the next step is so the next step is to create a lib for the edge store which is going to be our hook which we are going to use in all our client components to easily upload files from the front end so copy this file make sure you're selecting the app folder make sure and this is the front end section right and we need to create a lib called EDG store. TS so let's go ahead inside of our lib folder we already have utils inside and now we're going to create a new file EDG store. DX and paste everything inside great and now that we have that we have to wrap our entire app inside of the edge store provider so let's go ahead inside of our app folder so our root layout app folder layout right here and let's go ahead and let's import The Edge uh store Provider from atli Edge store so that file we just created and I'm going to go ahead and wrap it inside of the convex client provider like this and just here where it ends just like that let's look at the documentation if that's it and looks like that is it we are now ready to actually upload a file as you can see we can do it very easily we just need to upload Edge store from use Edge store and we need an input which is actually going to allow the user to select the file and then all we do is await Edge store public files upload and we pass in the file if we want to do some UI with the percentage of the progress that's been uploaded we can use this hook for it right here and if you look at the documentation even further you can see that this Edge store has a a functionality to replace a URL this is going to come useful with our cover image because we're going to have an option to replace an image so it's very cool that we have this we can just send the old URL that's going to be deleted and then we have the new one in that place and you can also delete delete files very easily but make sure that you also add the before delete life cycle hook on the bucket we're going to do all that let me just show you a bit more what you can do so you can also do temporary files if for any reason your UI is needed for a temporary file as you can see they will be automatically deleted after 24 hours if they are not confirmed and you have a nice little uh URL to confirm right here which you get and then you can use that to confirm an upload so just something if you're playing and using this in your uh own application great so now that we have that if you want to you can already try with this very simple example with an input with a set file here and then an upload or you can go ahead and set up this uh image component right here as you can see this is a drag and drop uh drops down here so I'm going to be using this single image component so let's go ahead and skip the usage for now of course you can take a look at it if you're wondering how this is going to look in the end but what I want to go is I want to go to the installation part right here and as you can see we have to install Tailwind merge react drop zone and Lucid react if I believe we already have lucid react installed as well as Tailwind merge but it doesn't matter if you want you can just do npm install react drop zone or you can just copy this entire line but you will get some new versions with this if any new versions came out by the time you started this tutorial but I think it's safe enough to do that so let's go ahead and let's do this so mpm install Tailwind Das merge react d drop zone and Lucid react go ahead and install all of these dependencies great let me just zoom back in and now let's go ahead and copy uh the following component inside of our components folder so it's right here this very big uh component here if for any reason uh you cannot find this or maybe the documentation changed in the future you can always just visit my GitHub and find this component which I copied from here you didn't have to worry about it and there are also much simpler ways of doing this if you don't want the drops Zone you don't have to use one but let's go ahead and click copy right here and let's go inside of our components folder and create a new file single- image- drop zone. DSX let me try and expand my sidebar so single- image- drop zone. DSX and I'm going to paste all of this inside great as you can see we have no errors meaning that we have all of the packages installed perfect so first thing that I want to do is I want to go inside of this input props and I want to make the height and width optional like this so you can just put a little question mark here because we're not going to have them have fixed height and width because ours is in a model and then what I'm going to do is I'm going to go ahead and import spinner from do/ spinner like this because they are in the same component sorry in the same folder the global components folder we have the spinner right here and the single image right here so we can just do dot SL spinner perfect uh and now what I want to do uh is I want to go ahead uh and change the way uh it shows the loading state so let's go ahead all the way down to where it actually returns something so right here and let's go ahead and give this main div right here a class name of relative and let's go ahead and create an optional disabled so if this uh entire single image drop zone is disabled let's go ahead and give it a div which is going to render our spinner component with the size of large and go ahead and give it a class name of flex items D Center justify Das Center like this absolute D absolute like that and we're also going to have inset dy- Z height full width full and BG Das background sl80 and Z index of 50 like that perfect and now let's go ahead even uh lower here and find this upload icon comment or this upload icon upload Cloud icon if it's easier like that and let's go ahead uh and modify this a little bit so I'm going to go ahead and remove this div which has this button here we're not going to need that and instead I'm just going to focus on this div right here and I'm going to change this text to be click or drag file to this area to upload like that uh if you want of course you didn't have to change anything inside of this file right but I'm just doing exactly what I want so it looks better in our notion clone perfect so again you can just visit my source code and copy the single image drop zone if you find this hard to follow uh it's always a bit more complicated with working with code we have to copy but that's completely fine uh let's go ahead now back inside of our models uh cover image model right here and let's go ahead and import that component right so I'm going to go ahead and import single image drop zone and I'm going to rename this to use at SL components like that and let's go ahead and let's create our state for the uploaded file so const file set file is going to be use state from react so make sure you add this this import and I'm going to go ahead and give this uh use State a file sorry a type of file like this and let's go ahead and write const is submitting set is submitting to be used state fulse in the beginning like this and now let's go ahead and let's import whoops let's add the edge store from use Edge store and we can get that from s/ lib Edge store like right here as I did it perfect and it's going to be that easy to add our upload now now that we have our uh Edge store sorry it's not Edge store with a capital S it's Edge store like this you can let type safety completed for you so I wrote it with a capital S so that's why I had a little error make sure you just change it to lowercase s great and now let's go ahead and let's create our own change function so const on change is going to be an asynchronous function which accepts a file which is an optional prop if it has a file it's going to go ahead and set is submitting to be true it's it's going to set file to be file and it's write const to be await EDG store dopu files. upload that specific file as easy as that and then let's go ahead and let's add our update mutation here so I will just separate uh let me just do it like this I'm just playing around so it look so it makes more sense okay so states are here and these other hooks are here and now let's go ahead and let's add const update to be use mutation from convex react so make sure you add use mutation from convex react and let me move it to the top here and separate those Imports and let's add our API from convex generated API api. documents. update great and now inside when we have this response here we can go ahead and write a wait update ID to be pams which we don't yet have so let's do con perams to be use perms from next SL navigation so make sure you add this input right here and I'm just going to move it here and we're going to use pam. document ID as ID which you can get from convex generated data model so make sure you import this as well and that's going to be a type of ID documents and the thing we're going to update is the cover image and that's going to be response. URL like that perfect and now let's go ahead and let's add our on close function so const on close is going to be an arrow function which set file to undefined set is submitting to false and call the cover image on close like that and then we're going to go ahead uh to the end of this await and then we're going to call the enclos like this perfect and now we're ready to replace this to-do and this div with our single image drop zone like this and let's go ahead and give it a class name of w- full outline dnone let's give it a disabled prop of is submitting and let's give it a value of file if we selected it and onchange to be onchange great so if we set up everything correctly this should be working so let's go ahead and prepare a couple of stuff I want you to go inside of your dashboard in the edge store and go ahead and select your project as you can see currently I have no files inside no buckin nothing and now let's go ahead and refresh my project here which I think I forgot to run or maybe because of too much changes it shut down so let me just find there we go looks like my project shut down so if that happened to you don't worry it happens in development especially when we do a lot lot of changes at once so just go ahead and mpm run Dev uh make sure you have your uh MPX convex running as well and let's go ahead and refresh this now let's wait a second and there we go it seems that the app is working let's click add a cover image and let's go ahead and select something so I just change the folder for my images and let's go ahead and I don't know select this image from unsplash let's wait and let's see if we have this Let me refresh this right here and there we go it says one file and we have the file uploaded right here we currently cannot see it so let's visit our dashboard our convex dashboard to see if it's stored in the database correctly so go to convex dodev go ahead and click on login and go ahead and select your project here and there we go cover image as you can see has has its own uh URL here meaning that it's successfully uploaded perfect and now what we have to do is as you can see we no longer have a button to upload a cover image because instead we should already be showing our cover image so that's our next step we're going to go ahead and render that cover image and add the buttons to replace it and to delete it so let's go ahead and visit our uh document ID page so I'm going to close everything here go inside of the app folder main routes documents document ID page. TSX and let's go ahead and remove this div which currently holds all this space and instead we're going to render our cover component which currently doesn't exist so when you save you're going to get an error so let's go ahead and add the cover not inside of here but inside of our main components because that's going to be reusable as well so add cover. DSX mark it as use client and let's let's export const cover and return a div sying cover image go back inside of the page. vsx where you added the cover and you can now import it from s/ components cover like this and if you save your errors should go away now let's go ahead and give this cover a URL prop to be document. cover image and let's go inside of cover right now and we have to create an interface for that so inter face power image props it's going to have a URL which is an optional string and it's also going to have a preview optional Boolean let's go ahead and extract those props so cover image props and extract the URL and the preview just like that now you should no longer have any errors inside of this uh page. DSX and instead we can focus on what's written here so let's give this div a class name which is going to be Dynamics so go ahead and import CN from lib utils and let's go ahead and let's first give it some default classes so that's going to be a relative it's going to be W full height of fixed 35 VH and group and now let's give it if we don't have the URL we can lower that space so let's give it a height of 12 VH and if we have an URL we're going to go ahead and write BG to be muted as well so for a second it takes to load it's going to take uh it's going to look like a little skeleton there perfect now let's go inside and let's check if we have the URL in that case so this is double exclamation point don't mistake it with a single exclamation point it's a different thing this will turn this into a Boolean which is right now a string or undefined so we're working with booleans here so if this is true meaning if we have the URL in that case render an image component from next SL image so just make make sure you import this I'm just going to separate those two Imports and let's go ahead and let's give it a source of URL itself let's give it a fill property an ALT of cover and a class name of object Das cover like this uh and let's see if we can already see something and we can see an error but this is a step in the good direction as you can see we have if you watch my tutorials a familiar error so it says that the next image host name files EDG st.dev is not configured in our next config JS so when working with images in nextjs specifically when using the image component from next SL image right and if you just use the regular IMG element this would not happen that's me you're using the optimized images from next SL image we have to add the host name where our file is inside of next. config.js so let's go ahead and look at this eror go ahead and find find where res sets the host name and find the host name right here for me it's files. edore dodev and we have to add that to next. config.js so let's go ahead and do that I'm going to go inside uh of my next. config.js inside of this object add images domains and go ahead and just paste that specific uh URL host name inside and my recommendation is that you also reset your server so go ahead and find your Local Host here looks like it's working you can see it found a change in next config but let me shut it down just in case and rerun it because I found some kind some weird errors are happening there perfect and as always uh let's go ahead and also do npx convex Dev again just in case you know uh well I I'm pretty sure this lives outside separate of the nextjs environment but I just want to bring your attention that we need to have this running as well in case you forgot about this terminal so go ahead and rerun it just to refresh your memory of it great and let's try and refresh now and if I'm not mistaken we should already be seeing our uploaded cover image for this document there we go our beautiful image is right here perfect and now we have to find a way to remove it and to replace it so let's go ahead back inside uh of our components cover right here and now let's go ahead uh and let's write if we have the URL and if this is not a preview in that case we're going to add some actions here so this is going to be a div with a class name of opacity -0 but when we hover on the uh this image we're going to have the opacity to 100 it's going to be an absolute class with a bottom of five right of five a flex box with items centered and a gap between them of two and let's go ahead and add a button so make sure you import button like this I'm going to change it to components great and let's go ahead and write change cover and let's give this an image icon from Lucid react so make sure you import that it's a self closing tag and give it a class name of h-4 W-4 and margin right off two and let's give this button an on click for now to just be an empty Arrow function and let's go ahead and write class name text- muted Das foreground text whoops foreground text- extra small let's give it a variant of outline and let's just just not misspell variant and give it a size of small and then you can copy and paste this button like that and go ahead and change this to be X from Lucid react so make sure you import X and let's go ahead and let's write remove like this uh and I think that should be it let's take a look now when I hover here there we go I have the option to change the cover or to remove it so let's go ahead and create this functions now so to change a cover we can just very simply add our cover image hook right so const cover image is use cover image and we can do that from s/ hooks use cover image and then I can just do find this first button and give it an on click to be cover image do on open like this and let's go ahead and try that so when I click cover image let's go ahead and select another image here let's wait for it to upload and there we go I successfully changed my image here and now let's go ahead and let's add the remove image from here uh so we're going to have to create a function for that and we're also going to have to create another API endpoint inside of our uh documents file so let's go inside of convex documents right here so very similarly to the way we created this remove icon we're going to go ahead and create our remove uh cover image so let's go ahead and write that export con remove cover image is going to be a mutation which accept the arguments to be ID of uh v. ID documents a Handler it's going to be a synchronous function which takes the context and the arguments we pass from above let's go ahead and let's do uh exactly what we did above so we get the identity user ID and let's also uh copy the existing document so the identity check for the identity user ID and existing document if we don't have the existing document in that case throw new error not bound if existing document user ID is not identical to user ID throw new error unauthorized and now let's go ahead and let's actually update the document so cons document await context. database. patch arguments. ID hover image is undefined oops and let's do return document great uh and now that we have that we can go back inside of our cover and let's go ahead and import the mutation so import use mutation from convex react and let's import our API from convex generated API and now let's go ahead and add the remove cover image from use mutation api. documents remove cover image great and now let's go ahead and let's create const on remove to Be an Arrow function which calls the remove cover image and just passes in the ID as pam. document we don't have the params so let's add cons pams to be used params from next SL navigation make sure you add this import I'm going to move it here and let's use pam. document ID as ID from convex generated data model because we need to use the proper type here which is documents ID perfect and now we can assign this on remove inside of this onclick right here for the remove button and let's see if that is working as well so when I click remove right here there we go I now can do uh add a cover again so let me just add a new cover here again from unsplash and let's see if it's still working there we go it's working and I can even expand my screen and you can see how it works as well it scales and everything I can collapse the entire screen and then it takes more space amazing amazing job now let's go ahead and let's improve our change cover and remove functionality so it works perfectly on the UI side but if we take a look at our Edge store you can see that inside I have three documents uploaded but only one actual file that I'm using so it will be nice if we had a cleanup function when we click remove and change cover replaces the old URL instead so what I want to do now is I want to go ahead and clear my entire schema sorry my entire table from convex uh database and I'm going to select all of the files here and I'm going to go ahead and empty the bucket like this and let me just confirm this so notion D tututorial SLU files and let's go ahead and empty these three files great so now we have no files inside and let's go ahead and go back to slash documents don't worry about this error we're going to handle errors later for now just go back to slash documents inside of your URL and let's go ahead and create a new page here and let's add a cover image here so I'm just going to use a random one let's wait for this to upload great so now we have a file here and if I refresh in the edge store I have one file here let's head inside of the documentation and let's see how exactly we can remove files so we're going to follow the documentation here we're also going to do the replace file but first let's do the delete file so we can delete a file by passing the URL of the file inside the delete method but in in order to allow that we need to set the before delete life cycle hook on the bucket so let's go ahead and click here and see what that's about all right so we have to find our Edge store router let's do that first I'm fairly certain that our Edge store router is located inside of API EDG stor route. DS right here so there we go it's EDG store router right here and let's see what life cycle hook we actually have to give it so here we have the before upload hook and we have the before uh before delete hook it says that by default every upload from our app is allowed so we don't need to add this hook I mean we already confirmed that uploading Works instead we need to add this before delete hook right here so let's go ahead and copy this let's remove this comma from here and let's paste it here so we have this before delete and in my case we can just return true right we don't have to even extract any params from here we can just simply return true so make sure you add this inside of your Ed Edge store router and then let's go back and see what we have to do for the deletion of the file so we have to use AWA EDG store. public files. delete and passing the URL to delete and we do that inside of our uh cover right here we have the on remove button so go inside of your cover component which is inside of component cover. DSX and let's go ahead and let's import Edge store from oops uh use Edge store from at/ Li Edge store let me show you that so at/ Li EDG store and I imported use Edge store by destructuring it and let's go ahead and inside of this on remove first thing that I'm going to do is I'm going to write Edge store do public files. delete like that and I'm going to pass in the URL to be let's see if we have the URL we have it inside of the cover so passing the URL like this and let's go ahead and let's wrap this inside of if we have the URL like this like this and let's go ahead and Mark this on remove to be an asynchronous function and put this inside of an await so this only runs after this has been finished let's see if that's going to improve our code and whether we did everything correctly so inside of our Edge store right now I only have this one file and if I go ahead and remove the file I should remove it from the database that obviously happened but let's refresh here to see if it removed from here and it is as you can see the file is successfully removed from here here as well perfect and now what we have to do is we have to improve this functionality that when we change cover it doesn't add a new image instead it uses this replace file method right here in order to do that first thing I'm going to do is go back inside of my uh hooks use cover image right here and I'm going to add a new method in the type to be on replaced and that's going to accept the URL which is a string and give it a void and then we're going to go ahead and add a we actually have to add another one which is URL to be a type of string and let's make it optional like this and let's now write on replace here to get that URL which is a type of string and let's go ahead and call set is open to be true and URL to be the URL which we pass from here here and on close let's add the URL to be undefined like that perfect and you can also if you want to add the URL undefined explicitly here even though you saw that we got no errors so you don't have to but if you want to explicitly write it you can do it here perfect and now let's go ahead inside of our components cover right here and when we call the uh this cover image we call the uh cover image on open instead let's do this let's change this to be an arrow function which calls cover image on replace and passes in the URL prop perfect so now when we open this we're going to open our model cover image model right here and in this cover image we now going to have that URL so let's go ahead and see how we have to do that so I'm going to check if cover image has the URL in that case I'm going to run something else and I'm going to mark this in an else function like this so let's go ahead and wrap this and instead let's go ahead and do it like this let's do rest and change this to not be a constant but to assign to that response and then do a rest here and let's go ahead and follow the replace file method so we have to write await Edge store dopu files. upload right here let's go ahead and pass in the file we want to upload and let's pass in the options to be replace Target URL to be cover image image. URL like this and it may be that we don't even need this if else perhaps we can just go ahead and add the options here but first let's try it this way so I'm going to go ahead and try that now let's go inside of here let's confirm that we only have one image here and now let's attempt to change the cover here I'm going to go ahead and change it to another picture and if our code is correct now there should still be only one but with a different URL let's refresh and there we go look at this the URL has changed and we have a working image here so our code is working and now let's try to improve it even further as I said perhaps we don't even need this if else so let's go ahead and do this instead I'm going to remove this I'm going to bring this back to be a constant and then I'm going to add options and what was the code I forgot the code was replace Target URL so let's pass in the replace Target URL and go ahead and pass in cover image. URL and looks like it's working because the replace Target URL can be undefined so even if this is our first time adding an image this code should work let's try out all of the cases now so again confirming your Edge store we only have one file let's go ahead and call the change cover and let's replace it with another file here so I'm changing a completely different image it's working let's refresh our Edge store there we go the URL changed and we only have one file now let's try and removing the file there we go we no longer have this let's refresh Edge store no fil is here and let's try now adding a cover image to confirm that is working as well so something we haven't pasted let's see if that's going to work and it looks like it's working when I refresh we have it right here perfect so we did not even need that if else Clause we can just pass in the cover image. URL beautiful amazing job and now let's try and create a new title just to confirm that we did not mess up anything so I'm going to go ahead and add another image from unsplash here just to confirm that none of this are replacing the other image so let me try and replace this child cover right here uh with some other image let's see this other image from unsplash looks like it's working this one did not change and I should only have two images inside and I do have two images let's try and remove this one from the child there we go this one is still intact and now I should only have one image perfect looks like it's working as intended and we successfully have the deletion of our files we have the replacement of our files and we confirm that individually they do not remove each other so our code is working perfectly fine great great job so now that we have our fully working cover image here um let's go ahead and I just want to add one extra layer of security um inside of this hook right here use cover image so I don't ever want us to accidentally replace an image so whenever we use onopen I'm just going to go ahead and set the URL to undefined explicitly like this so just in case we called the on replace somewhere and somehow we forget to reset that even though the onay enclose resets it to the URL to undefined it would be weird that it's it's left in the state and causes some unexpected events so I just changed on open to have URL undefined as well here and now that shouldn't change absolutely anything so I should be able to modify my screen right here let's see I have one file here and when I refresh I have another file instead and it's all still working it seems perfect let's go ahead and let's confirm inside of another file here when I add the initial image I should now have two images in my Edge store so let's refresh that that is working as well that seems to be working the last image was not uh misplaced let's try and replace an image from the other child here just confirm that that function is still working so I always like to be thorough when making these changes there we go still two images and let's try and remove an image and now it should be only one image great everything seems to be working fine perfect now let's go ahead and let's go in back inside of our cover. DSX so inside of the components folder and let's go ahead all the way to the bottom and let's write cover. skeleton to be a function cover skeleton which is going to return the skeleton from /ui skeleton and let's just go ahead and find where we import that and I'm just going to change it to add/ components UI skeleton and move it here to the top and let's go ahead and give this skeleton a class name of w- full andh of 12 VH like that and now let's go back inside of app main routes document ID page. DSX right here and let's focus on this document undefined where we did the loading and let's now import the cover. skeleton here and let's add a div here with a class name of MD maxw 3XL LG Max W4 Excel mx- aouto and margin top of 10 and inside let's add a div which is going to have a class name of space- y-4 padding left of eight and padding top of four and let's add the skeleton component again from at/ components UI skeletons let me show you I just imported that right here and let's go ahead and give this skeleton a class name of height 14 and W 50% and you can copy this a couple of times and let's give this one these others a height of four like that and let's give this one 80% let's give this one 40 and this one uh 60% perfect so now we have a nicer loading skeleton here and let's try that out so I'm going to make sure you refresh everything and as you can see when I go ahead and switch my pages you can see how for a second I have a nice little loading skeleton which looks just a bit better perfect and now we're finally ready to create our editor so let's go ahead and do that so we have to head inside of our terminal here and let's go ahead and let's do npm install at block note SL react and block note SL core like this so these two items let me try and expand this there we go so block not/ react and at blote SL core like this and let's wait wait a second for all of this um to install and then we're going to go back inside of this page. CSX and we're going to add our editor finally so let's just wait a second for this to finish great and now let's go back inside of page. DSX where we just rendered this skeleton right here and let's go below the toolbar and let's render the editor component we currently don't have it so if you save you're to get an error let's write onchange for now to just be an empty Arrow function and let's write initial content to be document. content like that and now let's go inside of the components folder where we have the cover the toolbar the single image drop zone right here and create a new file editor. DSX let's go ahead and let's mark the entire editor as used client and let's go ahead and let's import block node editor partial block from at block nodecore let's go ahead and let's import from at blocknote react block note View and use block note like that and let's go ahead and let's import the Styles so import at blocknote SL 4/ style.css like this perfect and now let's go ahead and let's write the interface editor props to have the onchange which accepts the value which is a string and a void we're going to have the optional initial content which is a string and editable to be an optional Boolean and now let's go ahead and Export const editor and let's assign this props here here so we're going to have the editor props and let's extract them so onchange initial content and editable and for now let's just go ahead and return a div saying editor now we can go back inside of page. DSX and we can import editor from at/ components editor so make sure you add this import and save the file and you should no longer have any errors and now you should just see a text which says editor right here let's go inside of the editor here uh and let's start uh developing this so I'm going to go ahead inside of the editor props here and I'm going to write const Editor to be a type of block note editor which we imported and that's going to use block note let's go ahead and write editable here let's give it an initial content to be initial content question mark json.parse initial content and we're going to give it a type as partial block and an array of it or undefined like this so make sure this is written uh in one line like this but let me just simplify it just so you can see it uh like this all right or you can just write it in one line as I did I just don't want to keep this zoomed out so you can see better and let's write on editor content change like this and you can write it like that you can make it as an object and then just give it an arrow function and don't forget to add a comma here at the end and let's do on change to use json. stringify like this editor. toop level blocks null and two just like this perfect and now let's go ahead and let's go ahead and render the block note view which is a self- closing tag let's give it an editor of editor let's give it a theme uh well we have to uh carefully assign the theme because it's not the same as we want the one we use inside of our code so let's write const uh resolve the theme to be use theme from next themes so make sure you imported use theme I'm just going to move it to the top all right and now let's go ahead and let's write if resolve theme is dark then we use dark otherwise use light like this and let's go ahead and see if we can already see this right here as you can see we have an editor now I can do a slash I can add a heading component I can go ahead and add a subheading I can go ahead and make a bullet list you can go ahead and select this and bold it italic underlying strike through it you can add colors to it you can make it nested you can unest it you can create a link if you want to you can also go ahead and move these blocks above another like this so everything you can almost everything you can do in the original notion and we are using this uh thanks to block note so you can always visit block note. JS like like this and you can see everything it has uh in its in its documentation you can also try it right here uh inside of this uh editor here or you can visit the documentation and you can customize it you can uh create different themes and styling you can format the toolbar if you don't like it you can modify the slash menu uh you can even add additional blocks on your own so a bunch of stuff you can do and this entire block note Ed editor is buil on top of Pros mirror and tip tab which are uh undoubtedly the best what you see is what you get editors in 2023 perfect so now that we have that there's one thing that we have to do here currently if we refresh right here nothing that we've written is saved and that's because we don't have the on change function so let's go ahead and let's create that uh on change function by going inside of page where we rendom the editor and let's go ahead and add const update to be use mutation from convex react so just make sure you add use mutation all right and we're going to import API documents. update like this and let's add Con on change to accept the content which is a type of string and go ahead and update the ID using pam. document ID and the content just like that and and copy this on change and now we can go ahead and just simply assign it to here on change let's go ahead and see how that looks now so now if I try hello world if I add a heading hello big world and if I refresh the entire thing as you can see it's still here perfect so you have a fully functioning editor here but one thing that we are missing is when we select an image here we can only embed by URL but how do we add an upload file well thanks to the combination of block editor and Edge store very very simply let's go inside of editor right here and let's go ahead and let's import use Edge store so I'm going to go here and I'm going to import use Edge store from /li use Edge store like this let's go ahead and let's extract The Edge store from use Edge store and now what we have to do is add a function con handle upload which is a synchronous function which takes in the file which is the type of file and all it's going to do is const response to be await EDG store dopu files. upload and pass in that file like that and return response. URL great and now all we have to do is add that inside of the used block number so we're going to do upload file to just be handle upload perfect let's go ahead and try it out now so I'm going to go ahead and refresh this all right uh and let me delete this block and try again so you can either write image like this and there we go we can now click upload and let's go ahead uh and do that so let me just find my assets and let's try and import something from here as you can see it's loading and there we go go look at our beautiful image from here and if you take a look at the convex dashboard you can see that we have content now and this content is just a very big uh string of uh stringified object right because it's too big to be defined as an object and this is how it's officially recommended from the documentation of block node to save it in the database perfect amazing job you just added the editor here uh great so what I want to do next is I want to go ahead and enable you to publish this note so everyone else can see it and also I think that if I currently go onto mobile mode right now H it seems like I can change this cover and remove it but the problem is this is all on Hover right so perhaps it would be a good idea to change it on mobile but maybe it even looks okay I don't know you can play around it with yourself but what I'm going to focus on now uh is go ahead and Publishing this but before we do that let's take a look at everything we left commented out so currently when I create a new page I'm not redirected to that page let's change that so I'm going to go back inside of my uh components folder navigation where is it uh it's app main components navigation right here and I think I commented out this function let's try and find it is it even here maybe it's not here let's try and find this so I need to find this new page which ises handle create where is it oh it's right here handle create uh okay and what I want to do is write do then and what we need to do is redirect to that page so we can just simply get the document ID from this callback and then do router. push and we don't have the router so let's go ahead and add the router here so cons router use router from next SL navigation so let's see where I imported that there we go use router from next SL navigation and then let's go ahead and find the handle create again and let's use the lowercase router. push let's use the back Tex SL documents SL individual document ID great and now if I go ahead and click new page here there we go I'm redirected to it and if I click here I'm redirected to it as well so let's just confirm there we go uh one place where I think this is not happening right now uh is if I go ahead and let's delete this forever if I click from here I'm not redirected into it so let's go ahead and resolve that so I'm going to close everything I'm going to go inside of the app folder main routes uh I'm going to go inside of documents page. DSX and in here the very simp thing we have the oncreate so let's go ahead and add then let's get the document ID and let's go ahead and add our router use router from next SL navigation let me just move it to the top with the others and let's do router. push backck SL documents and individual document ID great so now if I click from here it should be the same thing perfect uh and and let's see some inconsistencies here so when I delete from here I'm redirected to this screen but if I read but if I delete it from here I'm not redirected so you can choose the functionality you want right it doesn't matter but what I want to do is I want to make it equal I want to make it if I delete it from here I also want to get redirected away from it so let's find that I think that's located inside of the app main components item right here I think we have a function oh great so we have Theon create here as well so we have to fix that but let's first find the own archive like this so once we finish this let's go ahead and let's do router. push to/ documents like this so when I delete it from here I also want to get redirected so pick a random document make sure you selected and delete it from here and there we go we are redirected to here okay uh and now what I want to do is that when we click create on here I'm redirected to the child document that makes sense right so let's go ahead and find the oncreate inside of item component and I commented out this router. push because at the time we didn't have it so let's go ahead and see how that works now when I click plus here as you can see the child is immediately the new selected uh component perfect great job so what I want to do now is just go ahead and quickly fix how we are supposed to import uh the editor component so let's go ahead inside of editor component so components editor right here and I actually don't want to do export const editor instead what I want to do is just const editor and then at the bottom I'm going to do export default editor so now what I can do is go back inside of my app folder main routes document ID page. DSX where I use the editor and I'm not going to use this import right here uh instead what I'm going to do is I'm going to import Dynamic from next SL Dynamic and I'm also going to import use Memo from react so let's go ahead inside of our document ID page right here and let's write const Editor to be use memo open an arrow function and just add inde dependency array at the end in this Arrow function function called Dynamic and that Arrow function inside is going to have an import which is going to call the s/ components slash the editor like that but we're not done here we also have to pass in this option SSR PSE to it so let me try and zoom out so you can see this in one line here so we use use memo we call an arrow function which calls Dynamic which opens parentheses and calls its own import and then inside of the dynamic parenthesis we set the configuration to not use server s side rendering on it so this is the recommended way from the Block note documentation great so now that we uh have that uh I think we are finally uh ready well to handle our error state right that's one thing that you probably noticed we have not handled yet so I'm talking about this let's go ahead inside of a trash and let's copy the URL for this one and let's click delete forever and remove it and if I go back to it what happens is that we get an error well that's expected but I think we should handle it in a more graceful way so I want you to go inside of my GitHub and go ahead and find the public folder and you're going to need to download the error dark and the error.png files so let's go ahead and prepare our public folder so here on the side I'm just going to close everything and I'm to open my public folder and let's just expand this a bit so find the error.png and go ahead and download that file so I will drag and drop it inside of my public folder and now go ahead and download error dark so go ahead and download that file as well and drag and drop it inside of your public folder now just go ahead and confirm that both of these files are named correctly so it should be error.png and error - dark.png and now let's go ahead and close everything and let's go inside of our app folder and here in the root create error. vsx let's go ahead and Mark it as use client because every error component needs to be a use client component let's import image from next image and let's import link from next link and let's import the button from s/ components UI button and then we can write uh sfc error like this and let's go ahead and do a div here with a class name h- full Flex flex-all items D Center justify - Center and space- y-4 and inside go ahead and render the image so we're going to go ahead and render source error.png we're going to give it a height of 300 a width of 300 an Al of error like this and let's also give it a class name of dark hidden and then we can go ahead and copy and paste this image component and you can give this one an error SL dark and give this an error block but hidden on default great and now let's go below those these two images and add an H2 element which will say something went wrong and let's write class name text- extra large and font D medium and let's add the button component let's give it an as child prop and inside of it let's render a link component which says go back and give it an hre of Slash documents like this and make sure you do expert default error and there we go now our error screen is looking much better better so in development we can still see the errors which are happening but in production users will not be able to see this but they will be able to see this screen right here which when we go back leads to the working SL documents page great amazing job you're so close to being done here uh one more thing that I want to do now is just enable you to publish a specific page and then uh allow guests to view that page perfect uh amazing job and there's and after that we also going to do the deployment of course so now let's go ahead and let's create the publish functionality but before we do that I just want to quickly bring your attention that my icons in the nav bar are not exactly aligned as they should be you can see that this little button here doesn't exactly match up to this button here same with this icon it just seems a bit off uh and I just caught that so let's go ahead inside of the app folder inside of main components and pick the item. TSX right here and go ahead and find where we have this document icon if else clause and you can leave this one as it is but inside of this icon besides the height 18 pixels also give it a width of 18 pixels like this and save the file and now as you can see it's much more aligned uh one below another and it looks a bit better it was bugging me I know it's a very small detail but you know that's what we do uh in this channel we focus in the details great so let's go ahead now and let's create the publish button which is going to be right here next to my drop down for the uh uh deleting a specific document so I'm going to go ahead uh inside of the nav bar so you can find that in app main components nov bar bar. vsx uh right here and go ahead and find where you render uh the menu right here and above it go ahead and render the publish component which we don't yet have so you can save and you're going to get an error and let's go ahead and write initial data to be the document which we have inside now let's go and create that component so go ahead and create publish. DSX inside of underscore components in the main folder here let's go ahead and Mark this as use client export con publish to Be an Arrow function which just returns a div saying publish great and now we can go back to the nov bar and you can import that component from ublishing this to confirm nothing is going wrong anymore there we go we have a big publish text right here and and now let's go ahead inside of the publish and let's fix this typescript error which is telling us that we have the invalid props for it so I'm going to go ahead and create an interface publish props to have the initial data which is a type of document from convex generated module specifically documents schema and now let's go ahead and assign that so publish props and let's go inside of here and write the initial data great and now I want to create a hook called use origin because we're going to need it for this part so let's go inside of our Hooks and create a new one called use origin. DSX let's go ahead and import use effect and let's import use state from react and let's write export con use origin let's do mounted set mounted use state pulse because we're going to use the window object inside of here and that can cause hydration errors when it comes to serice side rendering and now let's write const origin is type of window not undefined inside of uh apostrophes like this so we're checking if type of window is not undefined in that case we're going to do window. location. orig question mark window.location do origin otherwise we're going to use an empty string and now let me just zoom out so you can see how this looks in one line so origin type of window is undefined is not undefined in that case we're going checking if we have the window location origin if we do we are using the origin otherwise we are adding the empty string great and now let's do use effect here set mounted to be true and give it an empty dependency array if we are not mounted just return an empty string otherwise you can return origin great and now let's go back inside of publish. DSX and let's import everything we need from our popover so import from popover uh add add/ components UI popover let's import the popover trigger let's import the popover and the popover content great and now let's go ahead and let's write some functions and some states which we're going to need for this so first let's get the origin from use origin which we just created which is at/ hooks use origin so make sure you add this import here now let's go ahead and let's add our update mutation so const update is use mutation from convex react I'm just going to move it to the top here and we are also going to need the API so import the API from convex generated API and it's going to be api. documents. update and now let's add some states so we're going to write const copied set copied use state from react false so just make sure you import use state from react and I'm going to move it to the top here besides the uh this one we're also going to have is submitting and set is submitting to be used State false let's get the URL to be origin slash preview and initial dataor ID so using this constant we are generating what is the URL that the user can share with guests to look at the published uh note we have so that's why we needed the origin hook so we can get whatever this website is being hosted on so if it's Local Host it's going to say Local Host if it's uh whatever HTTP something.com it's going to be HTTP something.com preview SL this great and now let's add the function to actually publish this so const on publish is an arrow function which does set is submitting to be true and then const promise is an update function which has the ID of initial dataor ID and is published is set to true and now let's go ahead and do the finally on this function and let's just do set is submitting to be false and now let's give it a toast here from sunar so make sure you import toast from Sunner I'm just going to move it here and inside of it here we're going to do toast. promise we're going to pass in the promise and we're going to write the loading state to be publishing we're GNA get the success state to be note published and we're going to write the error state to be failed to publish note great and now we can copy and paste this entire onp publish function and we can rename it to on unpublish and we're going to do the is published here to be false like this and we're going to change the toast to be unpublishing and this is going to say note unpublished and this will be failed to uh unpublish note great and one more thing that we have is Con on copy so we can copy the published URL that's going to be Navigator do clipboard. write text URL which we generated in this constant right here and then we're just going to say set copied to be true so we're going to show a little icon uh with a check mark if we successfully copied it and then we're going to wait out 1 second before we change the icon back to uh what it was originally so 1,000 millisecs is 1 second and we're just going to do set copy to be false here great and now let's go ahead and let's render our popover component and inside of here add a popover trigger which has and as child prop and inside of it we're going to render a button component so make sure you import button from add/ components UI button let's go ahead and write an end tag here and inside we're going to render publish like this and we're going to check if by chance we already published this so if it's published initial data that is published in that case we're going to go ahead and render the glob Globe icon from Lucid react so make sure you import Globe from Lucid react I'm going to move it here to the top so globe and it's a self closing tag like this let me just wrap this in parenthesis so I can uh do it in multiple lines and we're going to go ahead and give this globe uh a class name of text Sky 500 W of four and height of four and ml2 like this and let's go ahead back to this button now give it a size of small and a variant of ghost great and now let's go ahead outside of the trigger here and let's add the popover content let's give this popover content a class name of width of 72 let's give it an align of end let me just collapse this items here so align of end and and let's also do uh a line offset to be8 and force Mount now inside of here check if we already published this so if initial data is published in that case let's go ahead and create a div here for now which is just going to say published otherwise let's go ahead and create the uh screen which we're going to see first which is going to be a div with a class name of flex flex-all items D Center and justify Das Center and inside of this div uh let's go ahead and render the globe which we have imported and let's go ahead and give it a class name of h-8 w-8 text muted foreground and the margin bottom of two let's write a paragraph here which says publish this note and and let's write a class name of text small font medium and margin bottom of two and below the paragraph let's add a span element which is saying share your work with others and let's give this a class name of text extra small text muted foreground and also margin bottom of four and below that add a button which will say publish and this button will be disabled if we are submitting like that it's going to have an on click to be on publish and the class name is going to be full withth with a extra small text and size is going to be small and I think we can already see this now so let's go ahead and click on this publish here and there we go we have a popup which says publish this note and and share your work with others and if you click publish there we go now it should just say published and if you check your convex database so let me go ahead and log in here and let's go ahead and find the is published you can see that one of my uh documents right here is published so this is true for it perfect so it's working and now let's go ahead and you can see how I have a little icon here because it's published and now let's modify how this looks uh when it's actually uh published so we need to have the button to unpublish it and we also need to have an input where the URL is going to be written so give this div a class name of space y4 create another div inside with a class name of flex items Center and GAP X2 add a globe uh icon in here with a class name of text Sky 500 animated Das pulls and height of four sorry pulse height of four and width of four go ahead and add a paragraph This note is live on web give this a class name of text extra small font D medium and text Sky 500 and outside of this div create a new div with a class name Flex items D Center all right and inside of here open a native HT ml input like this is going to be a self closing tag give it a value uh of the URL which we have as a constant and give it a class name of flex -1 px-2 text extra small border rounded D l- MD so only on the left side is going to be rounded height of eight and BG muted and truncate and important it's also going to be the disabled and now right beside it we're going to add a button which is going to check if we have copied it in that case it's going to render the check icon from Lucid react so just make sure you import the check icon from Lucid react and this is going to have a class name of height four and width four like that and otherwise we're going to render the uh copy icon from Lucid react so make sure you add the copy icon as well and give this a class name of height four and width of four like that and give this button uh an on click to be on copy give it a disabled prop uh to be copied and give it a class name of height 8 BG sorry and rounded Das l-9 none like that perfect so let's take a look at how this looks let me just refresh this and there we go you can see how now I have an input and I can copy this now and if I try and paste this you can see that I have that copied preview URL you if you try and go there you're going to get a 404 we're going to do that in a moment and now we need to add a button here to unpublished note if we want to do that so let's go ahead and let's go outside of this div and let's just add a button here which is going to say unpublish and let's go ahead and give it a size of small let's go ahead and give it a class name of w full and text extra small let's give it a disabled prop of is submitting and let's give it an on click to be on unpublish great and now I think that should be it so let's go ahead and click publish here if we unpublish you can see that note is unpublish and it's no longer available but when I click on publish you can see that the note is live on web and we can copy that URL link perfect so now we actually have to create this route because if I go there now we're getting a 404 because the preview page doesn't exist so let's go ahead and do that so we can actually do that quite easily because we have all the components we already need there so let's go ahead and create a new app organizational router s organizational folder which is going to be called public so this is not going to have any of the layouts that we usually have like a sidebar or a off bar right so this is why organizational routes are so cool and now go ahead inside of here and create another folder routes and inside of it create a new folder preview and then the final folder which is going to be the IND individual document ID inside and then create a page. DSX and what you can do is just go inside of main routes document ID page. DSX and literally copy everything inside so this page which renders the editor using use memo and dynamic and has the prams you can copy that entire page go inside of public and paste it inside of this page right here and as you can see we have no errors with the Imports because this ones are reusable right so we have no problems with that but we do have to change a couple of stuff so first thing that I want to do is I want to give this cover a preview prop so it's not going to be editable same thing for the toolbar a preview prop and same thing for the editor here a preview prop sorry not preview but editable is going to be false like that perfect and now let's go ahead and try if this is already working so I'm going to click publish I'm going to copy this I'm going to paste it inside and what I should see is this very same document but without the sidebar I cannot edit I cannot add anything I cannot remove the image nothing perfect so this is exactly what we want and now let's just try it out if this works when we are logged out so I'm going to go ahead uh and copy this here and paste it in incognito mode where I'm logged out and there we go look at it it's work working right perfect and now let's go ahead and go back here and let's unpublish it from here so I'm going to click unpublish and look at this now we have an error because it's no longer working and well I just want to bring your attention the technically you can still go here where you're logged in and here I think you should oh maybe not no okay no worries I thought that maybe you can still see it from here but not uh great so now you can publish the route you can refresh from here and everything seems to be working but it looks like we have a little mismatch of colors in our dark mode uh so we're going to fix that of course but that is it we've finished all the functionalities we need for this project amazing amazing job all that it's left is to actually um deploy this entire application but before we do that let's just go ahead and go inside of our dark mode and see if anything needs fixing so it looks like here everything is okay but it looks like on preview this background is too dark let's take a look at this publish button uh this looks fine this looks okay this looks fine as well the trash we already fixed some stuff that looks all good let's go ahead and uh well everything actually seems fine it looks like great so let's go ahead in the preview and make sure this is published so let's just refresh this and let's go ahead and and fix the color and we can do that easily uh by going inside of the preview right here sorry inside of the public folder and just create a file layout. DSX like this so we reflect everything that's inside and let's go ahead and let's write sfc public layout let's go ahead and let's extract the children from it like this children rea react node and now let's go ahead and let's render a div with those children and let's give this div a class name of H full and dark background to be a hex code one F1 f1f like this perfect let's try it out now so I'm going to go back here and there we go looks beautiful exactly as we expect it to look like in light and dark mode amazing amazing job this was a very hard project we learned a bunch of different Technologies in this project uh and you completed uh the entire thing and all that's left to do is to deploy it let's go ahead and do the last step of this tutorial which is deploying the application so if you haven't already go ahead and create a repository for this application I did this in the beginning of the tutorial when I explained you how to use trunk but just in case you haven't done this let's go ahead and see how we do this so go ahead and open a new repository on GitHub and go ahead and give your repository name like next3 notion tutorial or actually I'm going to use Jan tutorial like this and I'm going to set it to private and I'm going to click create a repository and after this is done since this is not a new uh new repository right we have a lot of files here if you're going to select this second option push an existing repository from the command line and let me see if I can do that because I already have a repository uh inside of this so let's see if I can do this I'm going to go ahead and what you can actually do is shut down everything so you're no longer going to need the convex Dev or the mpm runev and just paste this commands from here and there we go I have an error because remote origin already exists but let's see if this command still worked or not let me refresh looks like it's not working but for you it's going to work if you haven't set up your repository beforehand on the other side if you already have the GitHub repository uh so just refresh this page and then you're going to see your GitHub repository so I already have it let me just show you uh where it is so let's go inside of my repositories and I have it right here notion dclone tutorial so this is my repository and now let's take a look at everything we need to do uh to prepare convex for production so click on Docs right here and find the production tab inside and you have the option hosting and deployment and select verell and let's go ahead and see what we need to do here so we need to create a versal account so do that if you haven't and try and sign up with your GitHub since it's going to be easier now we have to link our project on versel so click add new here and select a project and go ahead and select that uh repository so for my case it's notion dclone Das tututorial go ahead and click import here and I'm just going to change this to uh something else because I recommend you don't use the original application name inside of your url because it can be labeled as a fishing site so I'm going to change this to note taking app like this you can leave the framework pres it as it is and let's take a look at what we need to do next so it says we need to overwrite the build command so let's go ahead and do that click on build and output seconds and click on the overr right tab right here and change the Run command to be npm run build and convex deploy like this so npm run build and convex deploy and let's see what was previously it was next build so now it's mpm run build and convex deploy and now we need to set up our environment variables so first let's do it the easy way go inside of your environment. loal go ahead and copy everything from here and paste it in this one and there we go I have all of my uh variables right here but we're not done here because we have to change our convex project to production so let's go inside of our convex dashboard select the application you're working with and let's go ahead and choose production like this and there we go now it's creating our production and as you can see this is completely empty so let's try and find our uh URLs here so I clicked on uh settings right here and let's see what we need to do so what we need is the deployment URL right here so let's go ahead and copy this deployment URL let's go here and let's find that next Publix convex URL I think that is this one so let's paste it here so replace it like that now we have the convex deployment here uh and let's see if there's anything else we have to change uh let's try and generating a new deployment key like this there we go so deploy key right here go ahead and copy that and change the convex deployment to be the production instance of it like this and I think uh that should be it so in this case it's convex deploy key key but since we're working with nextjs I think ours is correct if it's not no worries and if we have any errors we're going to fix them so let's click deploy make sure you did this changes for production and I'm going to pause the video and I'm going to show you the results I have from the deployment so it looks like I have an error here and I'm pretty sure you're going to have it as well the deployment failed and it says that convex deploy key is not set so it looks like we made a mistake following this uh after all so let's go ahead and leave this as it is and I'm just going to zoom out and I'm going to click back here and let me just go ahead back inside of my projects here let me refresh this so I have this not taking app which I tried to deploy but it failed I'm going to click on settings here and I'm going to go on the environment variables here on the side right and let's go ahead and see so we have the convex deployment but we don't have the convex deployment key so let's go ahead and create that so copy this deploy key from here and we have to name it convex unor deploy unor key so let's add that so convex _ deploy undor key and paste it like this and click save there we go so now we have the convex deployment key like this and I'm just going to leave this as it is I'm not even sure if if we need convex deployment after all and now that you added the new environment variables go back inside of uh deployments here find the one that failed click on these three dots here and click redeploy and click redeploy one more time and I'm going to go ahead and pause the video again and we're going to see if there are any more errors we need to fix and there we go looks like it's working so now let's go ahead and click on no taking app here click on Project so you can get the URL which is no taking app- r.v. apppp in my case in your case it's going to be something different and look at this oh seems like we have a little bug here oh well we're going to fix that right away so I'm going to show you how to fix it but let's go ahead and check if everything is working so I'm going to log in with my GitHub from here I'm going to authorize clerk let's go ahead and there we go I can enter Jan and since we are in production we have no no Pages created here and there we go so I can start from the beginning here perfect real time is working let's test our upload if that is working as well so let me just find my folder all right let's select a random image from here let's see if that is going to work there we go that is working as well let's try it from here so I'm going to upload an image from here as well let's go ahead and add that there we go let's try and remove an image that seems to be working let's try and deleting this it's in the trash we can recover it everything is working as we expected let's add a little icon here there we go everything in our project is working the only problem we seem to have is in our landing page so let's try and resolve that so I'm going to go ahead and log out just make sure you have your app running again so let me just go ahead and do terminal here mpm runev inside of my project and let me just refresh this so I should be uh back on my landing page and there we go this is an error we have well not an error but it looks like our background is not fully uh dark here so let's see how we can resolve that first thing I'm going to check is the layout page of the marketing folder so I'm going to go inside of my app folder marketing here and I see I have a layout page and it looks like in here we do give it a specific dark color so let's see uh what else could we change here let's go inside of page. DSX to see that hm maybe here something is missing and yes let's try and give that dark background inside of the page. DSX as well like this let's see if that fixes it and that seems to have fixed it so how do you redeploy well very easily with versel all you have have to do is add this change commit this change so fix and get push like this go ahead and push that and now you can go back inside of your versel you can click on deployments and as you can see I have a new deployment building right now so I'm just going to go ahead uh and uh pause my screen and then we're going to visit the landing page again all right it's successfully deployed and let's go ahead and check check it out now so I'm going to go ahead and log out and let's take a look and there we go we no longer have that black bar here which we had everything is in the color it's supposed to be perfect amazing amazing job thank you so much for watching this entire tutorial and remember to leave a like share comment and subscribe and see you in the next one
Info
Channel: Code With Antonio
Views: 316,409
Rating: undefined out of 5
Keywords:
Id: 0OaDyjB9Ib8
Channel Id: undefined
Length: 473min 11sec (28391 seconds)
Published: Thu Oct 12 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.