In this chapter, we're going to go ahead and develop the dashboard sidebar. This will include creating the layout, module, the actual sidebar component, modifying the global CSS to obtain our theme, and then using those new themes to fix the gradient colors in our authentication views. So our sidebar will look something like this. A logo at top, meetings and agents items here, and then an upgrade item here. Down here we're gonna have the currently logged in user and the drop down for the user to log out.
Later, this dashboard sidebar will also have the free trial status, but it doesn't make sense to develop it now because we don't have any data to work with. So let's start with creating the actual dashboard layout. So as always, just make sure you have your app running npm run dev. And let's go ahead and check out how our app currently works. So right now go ahead and log in using either a social provider or email and password credentials.
And you need to be on your dashboard page. So basically this page with the big sign out button, which tells you logged in as whoever you're logged in with. So that is currently displayed on this route right here. Localhost 3000, just the root page. And it's this page right here, the home view, right?
So what we have to do now is the following. Inside of your app folder, we are going to create a route group called dashboard. And since you've already learned, whenever you create a new folder in parentheses inside of the next.js app folder, that means this is not a part of the URL. It is simply a grouping folder. And what you can do is you can actually drag and drop your main root page DSX and put it inside of here, like this.
If you're being asked to update the imports, you can select yes. If you still end up with one unsaved file, it's most likely the .next types app file, You can just go inside and click save and close it. And then make sure you close the .next folder so you don't accidentally continue developing here. The .next folder restarts and rebuilds every time you do npm run dev, so no worries. Even if you've messed something up, you can just delete this folder if you want to, and just restart your server and it's going to be rebuilt.
It's not going to affect the actual production of the app. So what we've done now is we moved our page.tsx from the app folder into the dashboard route group. So what changes now? Well, absolutely nothing. But what we can now do is we can create a layout which is specific for this dashboard route group.
So we don't have to use this global layout here, right? Because this is the root layout. This is something else. We don't want to add the sidebar here because I don't want to have my sidebar active at all times. I only want it active for sites or routes which belong in the dashboard route group.
So that's why we did this decision. Let's go inside of here and let's quickly create a layout. Inside of here, simply pass the children. And now let's go ahead and create the interface props children react react node. Go ahead inside of here and destructure the children.
And still, nothing should change because we don't add any new styles to this layout. But you should still test it just to confirm you didn't break anything. So what we're going to do now in the layout is the following. We're going to go ahead and change this to be a sidebar provider from components UI sidebar. So yes, we also added the sidebar components when we added chat CNUI.
Make sure to double-check that you have the sidebar component in your source components UI sidebar. If it's missing, make sure to revisit Chapter 1, where we actually set up chat CNUI and added components. Great. So now, we're going to go ahead and do the following. We're going to wrap our children inside of a main and give the main a class name of flex, flex column, height of screen, width of screen, and the background color of muted.
And then we're gonna go ahead and create a dashboard sidebar component. This will be created and maintained inside of the modules. So let's go ahead and create a dashboard module here. Let's go ahead and create UI and let's go ahead and create components and inside of here dashboard dash sidebar dot TSX like this go ahead and mark this as use client and now let's go ahead and import everything we need from components UI sidebar so the same path as this one sidebar content footer group group content header menu menu button and menu item just like that Now let's define our first section of items here. So it's going to be an array of items which have an icon, video icon, a label, which will say meetings, and an href, which will be the actual path, slash meetings.
Then we're gonna have an icon, bot icon from Lucid React, label of agents, and an href of forward slash agents. And then let's go ahead and copy this, paste it, and rename it second section. This one will just have one item, star icon from Lucid React, and the label upgrade, and an href forward slash upgrade. Make sure you have added all of these icons from Lucid React here. And now we're going to go ahead and render them so that they display just like this.
Let's go ahead and back inside of the layout. And actually, I can't really import anything yet. I first have to develop at least something. So let's export const dashboard sidebar. And let's go ahead and return a sidebar component.
Then a sidebar header. And give the sidebar header a class name. Next, sidebar accent dash foreground. And inside of here a link from next link. Make sure you don't accidentally import it from Lucid React.
Give this link an href of a forward slash and give it a class name of flex items center gap to px2 and padding top of 2. Inside the link, render an image from next forward slash image and give it an href of slash logo.svg. Basically, the same thing that we do in our sign up and sign in views. Logo.svg. In here, we used a normal image element because we needed to position it and style it a certain way.
But in here, we can use the optimized image component from Next.js. Now go ahead and give it a height of 36 and a width of 36 and an ALT of logo or Meet AI, whatever you want. And let me just see, it's not href, it's a source. Okay. And now we can already import this in the layout.
So let's add that so we have one less error. And you should already start seeing the development here. Make sure you're not zoomed in too much as it will probably collapse on mobile. So make sure you are in the desktop view. Great, so we now have that.
And let's add a paragraph here, meet AI, or the name of your app. And add a class name here, text to Excel and font, semi-bold. There we go. We have our text here now. Perfect.
Now let's go below this and add a class name, div with a class name, px4 and py of 2. And inside a separator. There is a sidebar separator component as well, like this. But I had some problems with it. Maybe it's something, the way I'm using it, it always overflew here for some reason.
So that's why I'm not using it in case you were wondering. And let's go ahead and give the text the separator a class name opacity 10 and a text of X color 5d6b6a. After you've done that, go ahead and save. And I'm not even sure if you will see this because it's extremely, extremely thin I can barely see it but you know just for your sanity you can add black can you do that text black or maybe let's see I'm trying to make sure this is actually visible. Let me just see.
Opacity 10. How about that? Opacity 100. There we go. Okay.
So Yeah, it's definitely here. Bring it back to what we wrote here. The reason I'm doing it like this, or it's barely visible, is because later we are going to obviously inverse these colors. So this is how it's going to be visible. Obviously on a white background, You can barely see it, but later when we inverse the colors, it will have more sense.
Great. So now, let's add a sidebar content, sidebar group, sidebar group content, and sidebar menu here. Inside of here, map the first section of our items. So get the individual item and go ahead and render sidebar menu item here. Go ahead and give it a key of item href.
And inside render a sidebar menu button. And inside of here, we are going to add a link component. We already have it great. And let's add an href here to be item href. And then what we can do is add a span here, item label and give this a class name text small font medium and tracking tight let me just indent this and now you should see meetings and agents text here and when you click on them they should lead you to 404 because we don't have them yet but if you take a look at your URLs they should be correct so what's missing is to render the actual icon here.
So let's go ahead and do the following. What we can do, let me try and do this. Can I just use this item icon and render it like this? I can. There we go, like that.
Let's just go ahead and style it a bit better. So I'm going to give this a class name of size five, like this. And this should just make it a little bit smaller, but you can see it's still kind of messed up, not exactly behaving as we want it to. And what I actually want to do now before we do anything else before we fix this is I want to go ahead and actually apply the proper theme for our dashboard here. So let's go ahead and go inside of our source app folder, globals.css.
And what I want you to do is I want you to scroll down and find the root. Now be careful because they have identical variables for root and for dark. We are not modifying dark. We are just modifying the root. Okay?
So the first one you're going to modify in your root is the primary color. Now, what I like to do is I like to leave comments so I know what I modified. And I also add a little explanation of what it is. This one is pretty self-explanatory, but still. Primary color, for example, the color of our buttons.
So instead of 0.2500, we're going to set it 0.63. The second value will be 0.1699, and the third value, 149.21. Make sure that you don't add any commas here. This is not RGB. Okay.
And right now, what you will immediately notice is that our buttons have a new color. So yes, now our button has a new primary color. Previously, it was a black color. And now our button is this nice green color matching our app. So that's the first thing I want us to change.
And now make sure you're logged in and seeing the sidebar. I want you to find these sidebar elements here. And let's modify the sidebar one. So I will mark this as sidebar background color so I know what it is and that I've modified it. It will be 0.2.
The second value will be 0.0283 and the third one 174.92. And you can already see the background color has now changed. Below that we have sidebar foreground, which will be sidebar text color. Go ahead and change this to be 0.82, 0.0057, and 182.99. So now the text is more visible towards this dark background.
We are going to leave the primary and the primary foreground as they are and go to sidebar accent. This is sidebar active item background color. And go ahead and change this to be 0.34, 0.0601 and the last one 171.21. And the last one we are going to modify is the sidebar accent foreground which is basically sidebar active item text color. Go ahead and modify this to 0.34, 0.06.
My apologies, no, I was reading the values from here. The sidebar active text color will be 1 0 0, a very simple one. So now that we finally have our colors set, what we can do is we can go back inside of the dashboard sidebar and we can modify the sidebar menu button to have some nicer colors here. So go ahead and add a class name here and import CN from libutils. This will allow us to dynamically change class names if needed and also avoid any merge conflicts.
So this is the util that was added with chat-cn. I think this was one of the first functions we looked at once we added chat-cn and I told you it was going to come in handy later on. So cnUtil is a way you should merge your class names, right? So the way you use it is pretty simple. It can accept an infinite amount of parameters.
So it can be, for example, text rows 500, and then the second parameter, background blue 500. Or you can add all of them in one parameter, depending on what you need, right? So, here's how I like to use it. I like to reserve the first parameter for my default classes. So, for example, height 10.
On hover, background linear to right slash OKLCH. Give it a border, border transparent. On hover, border will have a 5b6b68 color, divide it by 10. We are continuing our... And this is a typo.
It's not linear, it's linear. Make sure that when you hover on your class names, you can actually see the tailwind, I mean the CSS. That means it's a correct variable. And again, make sure you have the tailwind CSS in Talion Sense installed so you can see that. Let's continue.
So from sidebar accent, which we have now modified, right? From 5%, like this, via 30%, via sidebar color, but let's reduce it for 50% opacity, to sidebar color, but let's reduce it for 50% opacity to sidebar color, but let's reduce it for 50% opacity as well. So now when you hover, we have this nice effect. As you can see, it's brighter here and then it goes dark here. So it's a very cool looking effect when we hover on our sidebar items here, like that.
And let's go ahead and also do one important thing. So the reason our sidebar menu button looks broken is because we forgot to add as child here. Once you add that, there we go, you will see how it becomes a properly aligned and it's entirely clickable, right? You don't have to click directly on the label. Perfect.
So that is now added. Now, what I also want to do is the following. I want to know if a certain route is active, for example. So the way we can do that is by adding a const pathName, usePathName from nextNavigation. Make sure to add this import here.
And then we can simply compare the pathName with the item href. So for example, I just added this. Obviously, if I were just to add this type of class, I wouldn't need the CNUtil. But since now I'm going to add a comma here and add a dynamic one, it makes sense. So if pathName is identical to the item href for this specific item that we are iterating upon, we can simply add a different color, background linear to write OKLCH, and then a border 5D6B6 eight, divided by 10, like this.
And we can try it out, for example, you can do, you can comment this out and then just set const path name to be slash meetings. And then you will see that the meetings will kind of be active like this. But I don't want it exactly to be like this. Oh, yes, because we are missing one more prop here. So let's add is active here.
That name is identical to item href. There we go. So this is how it will look like when meetings is selected, or if the agents is selected. Did I write this properly? Agents, forward slash agents.
I'm expecting the agents to be selected, but looks like they are not selected for some reason. Let me just pause a bit and debug to see what I'm doing wrong. Oh, well, I figured out what I'm doing wrong. I'm using pathname to be like identical string, but it's not identical. It's missing.
It has an extra space here. So that's not the same string. Yeah, there we go. Now it's working. So that was the mistake.
Great, we can now remove the hard coded path name and make it dynamic like this. So naturally, none of these two will be selected because we are on the root page. And if you're wondering where is the root page in the sidebar, we are not going to have the root page. We are later going to add a rewrite in the next.js config to always lead us to forward slash meetings as the default page. But we don't need that now.
We are okay with having this kind of root page for now. Great, so we have this. And now what I want to do is I want to add the second section, and we can do this quite easily here. So what we need is we need to copy the entire sidebar group. Actually, I think, let me just see, do we copy the entire, yeah, let's just copy the entire sidebar group like this and let's paste it below And let's just add second section here.
So a bit repetitive, but there we go. Now we have an upgrade, which is clearly separated here. And in between these two groups, what we can do is we can copy this and we can add it here. And there we go. Now we have this kind of separator here as well.
Great. So that is now semi-finished. What I also want to do to wrap up the chapter is implement the user button. So the currently logged in user, right? So the way we are going to do that is by going to the end of the sidebar content and adding the sidebar footer.
Let's go ahead and give this a class name of TextWhite. And let's go ahead and add a div here with a class name. Actually, I don't think we need this. We can just do normal user button like this. So this is a component which we're going to have to implement now.
And actually, we might call it dashboardUserButton, like this. So we can go inside of the modules components and create dashboardUserButton.tsx because it's not exactly going to be reusable anywhere other than the dashboard. So in here, what we're going to do is we're going to export const dashboard userButton, return div userButton, and import dashboard userButton here. From .slash userButton it's completely fine because they are in the same folder. So now here at the bottom you should have userButton.
In case this next.js action button is getting in the way, you can actually click on it. You can click on preferences here and it can change the position, for example, to be bottom right, which will move it here, which is okay because we won't really have anything here. So if you want to you can do that so you can clearly see the user button here. Now let's go ahead and focus on the dashboard user button and let's go ahead and develop it. So what we have to do inside of the dashboard user button is get the data and is pending from out client use session.
Make sure you import this. So if a spending or if there is no data user, we can just return null, like this. So for a split second, this will not be visible, as you can see. If you want to, you can add a loading indicator here, however you prefer, but I'm completely okay for a split second, this just not existing until it loads. I've seen that kind of behavior on a lot of websites.
So now let's go ahead and do the following. We have to import everything from dropdown menu. So let's add all of these things. Drop down menu, content, item, label, separator and the trigger from components UI, drop down menu. So I'm going to encapsulate this inside of the drop down menu here.
I will add a drop-down menu trigger component here and I will give it a class name. RoundedLargeBorder, border-border with 10% opacity, padding 3, full width, flex, items center, justify between, background white with 5% opacity, on hover, background white with 10% opacity and overflow hidden. And inside of here, what we are going to do is the following. Let's just leave the user button now, and you will see how it looks like. You can zoom in a little bit, but just make sure your sidebar doesn't collapse if it's hard for you to see.
There we go. So now what I want to do is I want to display this user's image. So you can search for data.user.image and if we have an image you will display an avatar. If we don't you can just display null. We will actually render something later.
So for now, let's go ahead and leave it like this and add an avatar from components UI avatar and add avatar image from components UI avatar. You can move this here as well. You also have this installed when you add a chat CNUI. And the image will be source data user image, like this. So since I've logged in with my GitHub, you can see that I have an image here.
If you logged in using credentials, you will most likely not have an image here. So that's completely normal, don't worry. What I want us to do now is implement a little component, which will be useful for us to create placeholder images. It will be useful for two reasons, for users who don't have an avatar, and second, for our agents, right? Our agents will be given some kind of personality with a generated avatar.
So let's go ahead and go inside of components and create a new file called generated-avatar.tsx. So you don't have to put this in the UI folder. I like to keep chat-cn only stuff inside of here. Instead of the generated avatar here, we're going to have to install two packages. So the first one will be at dicebar-core, my apologies, forward slash core.
And the second one will be add dice bear. So it's dice bear, not bar, slash collection. Make sure to correct the dice bear if you haven't. And I'm pretty sure we need to add dash dash legacy here, depths to avoid any errors. I will, of course, show you the exact versions that have been added to my package.json here.
So let's go inside of package.json. And let's go inside of, there we go, dice bear. So 922, 922, like this. So now we can go back inside of our generated avatar here and we can import... Oops...
Create avatar from DiceBear core and we are going to import two collections from DiceBear collection. The first one will be bots neutral, and the second one will be initials. So basically DiceBear is an amazing library, which you can either use it with their JS API, their react API, they have a bunch of solutions, they even have an HTTP REST API, right. But this one is, this one is cool, because it actually generates the SVG without needing an internet connection. So it's not going to generate any fetch requests in our network to display these avatars.
And there's a bunch of collections you can explore. You can see there's a ton of them. The ones I've chosen here have a license which is free for personal and commercial use. So those are the initials and bots neutral created by Pablo Stanley. So amazing, amazing libraries.
So after you've added this, Let's go ahead and import CN from libutils and let's import avatar, avatar fallback and avatar image from add slash components UI avatar. And in here, create an interface generated avatar props seed will be a string class name will be an optional string and the variant will either be where you can just copy bots neutral or initials like this. And then export const generated avatar here. Go ahead and destructure the props here. So that will be seed, last name, and variant.
Just like that. And then define the avatar here. So let avatar, if variant is is equal to BotsNeutral, avatar will become createAvatar BotsNeutral and simply pass in the else avatar will be createAvatar initials, passing the seed, passing the font weight, and passing font size like this. Now that you have the avatar, what you can do is you can return avatar, avatar image, and avatar fallback here. Give the image a source of avatar to data URI and an alt of avatar.
And for the fallback, we can just use seed.characterAt first one to uppercase. And give the avatar a class name of CN and pass in the class name inside. So we can modify it from the outside. So now we have a component we can use whenever we need a generated avatar. So let's go back to our dashboard user button here.
And what the alternative here will be instead of null is the generated avatar component. And passing the seed to be data user name, and the variant will be initials. And class name will be size 9 and MR of 3. Initials, like this. So now nothing really changes for me.
But if I, for example, sign out, and if I go ahead and create a new user, for example, So mark mark, demo.com and sign in. So this one does not have, you can see any image, we use the generated avatar with their initials here. So that's how this will be used. Or you can change it to a boss neutral. And then it will create a little bot, right?
But that makes more sense to use for the actual agents, right? Great, so that's one thing that I wanted for the trigger, but still, let's go still inside of the dropdown trigger here and create a div with a class name, flex, flex column, gap 0.5, text left, overflow hidden, flex 1, and a minimum width of 0. Inside, we're going to have data user name and we're going to have data user email rendered here. So give the first one a class name of text small truncate and width full and the bottom one a class name of text extra small truncate and with full. And there we go.
Now you have your name and your email right here. Outside of the trigger, we're going to have a dropdown menu content here. This will have an align of end, side of right, and class name of width 72. And inside of here, drop down menu label with a div, class name, flex flex column, and gap of one. And then inside of here, you're going to have two span elements.
The first one will be data dot user dot name with the class name font, medium and truncate. And the bottom one will be data user email again, with the class name text small font normal, text muted foreground and truncate. So now when you click here you can see how it will open additional options here in form of a drop-down. Great! And one thing I forgot to do here in the drop-down menu trigger before you close it just go ahead and add a Chevron down icon from Lucid React.
Give it a class name, size four and shrink zero. Make sure you've added the lucid icon import. There we go. So now it has a drop-down button. Perfect.
So now let's go ahead and continue developing the drop-down menu content here. So After we finish with our label, add a drop-down menu separator, a self-closing tag, and then a drop-down menu item. In here, add billing and credit card icon from Lucid React. Copy this, and this one will be logout icon with the text logout. Give both of these icons class names and size 4.
Like this. And you can give both of these drop down menu items, class names, cursor pointer, flex items center, and justify between. There we go. So this is how the buttons are looking. And we can't really develop the billing one, but we can develop the onLogout one.
So above the return here, const on logout, asynchronous method, await out client, sign out, fetch options on success. And we also have to add the router here from useRouter.nextNavigation like this. And let's do router push forward slash sign in. And this actually does not have to be asynchronous. We don't have to wait it at all.
Let's use on logout and let's add it to the on click here. There we go. So now we have a user button, which you can see takes some time to load. Once it loads, we can click log out and we are logged out. Perfect.
So what I want to do now is I want to wrap it up by fixing the gradient colors here because we have added the layout, the modules, the sidebar, and we modified the global's theme. And now let's use these new variables to fix the gradient colors in the ALF views. So this part right here. So go inside of this sign-in view in your modules ALF UI views and find the second column where you render the normal image tag with logo SVG. Where you have the background radio.
And instead of using these colors, we're going to add our new variable colors. So from sidebar accent to sidebar. There we go. So now it matches our sidebar. Technically, we are using the sidebar color variable for something that's not a sidebar, but I think it's okay to borrow it for just one or two more places.
Let's go ahead and copy this and do the same thing in the sign up view so that they look the same. There we go. So now both our sign up and our sign in forms look very nice. Great. So I'm going to go ahead and wrap up the chapter now because we just fixed this.
There we go. And now let's create, review and merge our pull request. So what I'm going to do is close everything, open my source graph here. You can see we have a lot of changes here. Let's click on the branch down here.
Let's click create new branch. And let's call this 06, or is it 05? I'm not sure. 06 dashboard sidebar. Let's add all changes.
So they are staged. And let's do 06 dashboard sidebar. Make sure you are in your new branch here, click commit and publish branch. There we go. Now let's go to GitHub.
Let's open a pull request and let's wait for our reviewer to finish looking at our code. And here we have our code summary. So new features. We introduced a dashboard layout with a sidebar navigation and main content area. We have added a sidebar component featuring navigation links, section grouping and active route highlighting.
We've implemented the user profile button in the sidebar. We dropped down menu for options for billing and logout, as well as the avatar generator component. So in here we have a file by file walkthrough and a deeper review of what we've done. Two sequence diagrams, the first one explaining how our dashboard layout actually works. And the second one is our dashboard user button, which handles the sign out functionality.
We'll have some actionable comments. The first one is to add an on error in case sign out fails. This is definitely something we can do. And we will come back to this when we add the actual toaster component for displaying errors, right? So very good suggestion here.
In here, obviously, they suggest reusing some of the styles because we have two identical section groups, you know, first section and second section inside of our dashboard sidebar. But since we know it's only going to be used for two things, I'm okay with reusing it. If it turns out that we will add more sections in the future, it will probably be a good idea to separate it like they suggested here. In here, it advises against using hard-coded colors like this, which is a completely valid claim. So we can consider adding this to our global CSS.
We'll see if we can find at least one more place to use it perhaps. But for this one of cases, I'm OK with hard coding it. Anyways, great, great suggestions. Here you can, of course, decide for yourself. Your project does not have to be identical to mine.
If you want to handle some things that AI suggests, feel free to do so. So I'm going to go ahead and merge this pull request and once I've done that, I'm going back inside of my IDE here and I'm clicking down here and going back to my main branch. Make sure this says main or whatever is the name of your default branch and click on synchronize changes and click OK and this should automatically synchronize everything here so you can check by going inside of the app there we go layout dashboard sidebar everything is here amazing so that wraps up this chapter amazing job And see you in the next one.