So now that we've wrapped up our navbar, I want to go ahead and create this dashboard root page right here. So let's go ahead inside of the app folder dashboard page.tsx and let's go ahead and give this a class name of bg-red500 so we can see exactly how much space we are taking. So now I also want to give it a flex1 option and I want to give it a height and calculate 100% minus 80 pixels like this. And let's also give it a padding of 6. Like that.
There we go. So now you can see that our root page right here takes this entire space but it doesn't take the height of the navbar because we reduced that from the full height. Perfect. Now what I want to do is I want to add an example of an empty state. So I want to handle all empty states first.
First one is going to be when the user doesn't have an organization. So for that you can find any image you want or you can go inside of my GitHub repository, go inside of public and find elements.svg like this. So this is a part of a 3D illustration pack called Sally. I'm going to leave the link in the description for the full pack as well and it is allowed to use in both commercial and personal products. And now just drag and drop that inside of your public folder and just make sure it's renamed to something like elements like this which you can then type and add as an image.
Great! Let's head back inside of our app folder dashboard-page.vsx let's remove this bg-red500 and let's go ahead and forcefully show the empty state for now. So I'm gonna add empty org like that and now let's go inside of our underscore components here and let's create a new file empty org.tsx and I'm going to go ahead and export const that component so that we can import it in page and fix this error which we are having so empty organization from slash components empty organization and there we go, now we have a text which says empty. And now I want to go ahead inside of here and I want to give this div a class name of HFull-Blax-BlaxCall-Items-Center-Justify-Center. Like that.
Flex, flex, call, items, center and justify, center. Like that. And now inside of here I want to render an image from next slash image with a source of slash elements dot SVG, an alt of empty, a height of 200 and a width of 200 like that. And there we go, you can see how Now we have this kind of an empty element here and now we're going to add some information about what's actually this supposed to represent. So let's add an h2 element.
Welcome to and then the name of your app. So I'm going to call this board And let's give this h2 element a class name of TextToExcelFontSemiBold and margin top of 6. Like that. There we go. And now we're going to add some information text below that.
So in here let's add a paragraph. Create an organization to get started. And let's give this a class name of TextMutedForeground, TextSmall and MarginTopOf2, like that. And now I want to go ahead and add a button here, which will open this dialogue to create a new organization one more time. So let's go ahead and import everything we need.
So we need create organization from clerk next JS, and we need the button from components UI button, and we also need a set of components from our components UI dialog. So that opens up in a dialog. So dialog, dialog content, dialog trigger, like that. Let's go ahead and below this paragraph here, add a div with a class name of margin top six. And let's add a dialogue, let's add a dialogue trigger here, which is going to wrap our button.
So let's give this an as child prop so it doesn't mess up the styles or cause hydration errors. And we're just going to say create an organization. Actually, let's just write create organization. Like that. And I'm going to give it a size of large as well.
Like that. And then below that, let's set a dialog content and let's render the create organization component and let's give our dialog content a class name of padding 0, BG transparent border none and max width of 480 pixels in square brackets like that. So now this is how it's going to look like if this is the users first time joining and they haven't created, been invited or selected any category. So, sorry, organization. So then that when they click here there we go they can create a new organization perfect so we have that working as well when I refresh we're gonna see that new organization right here great And now I only want to show this if the user doesn't have any organization selected or if it doesn't have any organizations at all.
So we can do that quite easily by going back inside of our dashboard page here and we can turn this entire thing into use client. Since we're using convex, we don't need to do the whole React server components in this tutorial as much as we focus in that in other tutorials. So this one is gonna be a bit more familiar for the single page application folks. And in here let's add use organization from clerk nextjs. Like that.
And let's go ahead and let's extract the organization itself. And then what I'm going to do is I'm going to dynamically render this. So if we don't have an organization, so exclamation point organization, then I'm going to go ahead and conditionally render empty organization component. Otherwise I'm going to add a paragraph which will say board list. Like that.
There we go. You can see how now it says board list because I do have an organization. But if it happens that I create a completely new account or if I delete this organization, Let me just copy the name and delete this organization. There we go. This is how it's going to look like.
So when there is no organization selected, then it's going to prompt you to create an organization to get started. Perfect. So we wrapped that up. Now what I want to do is I want to create the empty states for when we actually have an organization like an empty board list. I also want to do a different empty state for when we search for something and a third empty state for when we click on favorites right here.
So we need to create three more empty states. So let's go ahead and create this board list component. But before we do that I want to show you that we can actually access the parameters like favorite and the search from our URL every time that we are working with a page convention component or a layout component. So what we can do here is we can create an interface dashboard page props and in here we can import search params, sorry we can destructure search params to have an optional search which is a string and favorites which is a string as well and once you do this just make sure and confirm that inside of your search input, you are using the search query. So that should match what you're trying to structure here.
And in your organization sidebar, when you click on favorites, you add favorites to the query. So just make sure there are no typos because this is what we are gonna be extracting from the URL. And then you can assign that here. So dashboard page props, search params, and let's go ahead and let's JSON stringify search params like that and there we go you can see how it says favorites true because I'm on favorites if I go back here nothing is inside of my query but if I search for something there we go my search is a search perfect so just confirm you have those because we're going to use them to pass them to our board list component which we're going to create now. So instead of this paragraph we're going to add a board list component and we're going to go ahead and pass in the organization id as a prop to be the current organization.id and the query is going to be search params.
So we know how to load the data. And then let's go ahead inside the underscore components and create a new file, board-list.tsx. And inside of here, we're going to mark this as use client. And let's create an interface, board list props to accept an organization id which is a required string and a query which is going to be required but the props inside are gonna be optional like search and favorites. There we go and then let's export const board list.
Let's assign the props, board list props, and let's get the organization ID and the query itself. And then inside of here, we can just return a div and let's do JSON stringify query one more time to confirm that this is working. So I'm gonna go back inside of my dashboard page.tsx and this time import that the same way we did with an empty organization. And right now there we go I can see an empty object if I click on favorites the favorites are here if I search on something the search is here great so let's head back inside of the board list and this is what I'm gonna do I'm gonna create a constant called data which is gonna be empty for now and this is gonna be to do change to API call. So for now we're going to pretend that we always have empty data and then what we can do is we can create different empty states.
So if we don't have data length like that but we have query.search that means that the fact that the data is empty is because users searched for something that doesn't exist. So we can change our return to say try searching for something else, for example. And then let's go ahead below that and let's add, if again we don't have data length and if we also have query.favorites, in that case, we can return a div saying no favorites, like that. And we can also use the third one if there is no data length at all, we can just use return and let's add a div, no boards at all, like this. So the order of this actually matter, right?
If you just copy and paste this at the top and put it above this then this would always initially render regardless if you have favorites on or search for anything so the order of this matters or if for any reason you want to change the order then you're going to have to be more specific with this query. So if you don't have data length, and if you don't have query.favorites, and if you don't have query.search, but it's just easier to make this an last if clause. So it is only gonna show if none of these are true. Perfect! So let's try that out now.
So as you can see, because I have something in my search it says try searching for something else. But if I completely remove it, it says no boards at all. And if I click on my favorites, it says no favorites. So confirm that you have all three behaviors working. Perfect!
And now what I want to do is I want to copy the assets which we're going to need to create these three separate empty states for an empty search, for empty favorites and for empty boards. So head inside of my GitHub repository right here in the public folder and go ahead and download empty favorites which I've prepared here. So just download this file. Then besides that also download empty search and then also download note.svg. I forgot to rename this one but I'm going to keep it as note.svg so that you can find it as well.
Let's just wait for this to load and download for me. There we go. So now I'm going to drag and drop all of those inside of my public folder here. So Let's go ahead and drag and drop note.svg, empty search and empty favorites. There we go.
So make sure that you have all of those here. Let's just take a look. So we have empty favorites, empty search and we have note.svg. Make sure all of them are named appropriately. Great and now let's go ahead and let's create these different elements.
So inside of our dashboard underscore components right here I'm gonna create a new file called empty search.tsx like that and inside of here I'm going to import image from next image and I'm gonna export const empty search and very simply return a div which renders that image which is a self-closing tag and let's give it a source of empty-search.svg. Let's give it a height of 140 and a width of 140 as well and an alt of empty like that. And then an h2 element no results found. Let's give this a class name of text to Excel, font semi-bold and margin top of 6. And let's add a paragraph, Try searching for something else.
And let's give this a class name of TextMutedForeground and TextSmall and MarginTop of 2 and let's give this wrapper div a class name of hFullFlexFlexColumnItemsCenter and JustifyCenter like that. And then we can head back inside of our board list component and in the first case here where it says try searching for something else instead we're gonna render empty search like this. There we go. So now when you search for something and if our API returns nothing, this is what will appear. But if you remove it, then it's gonna be a different empty state.
If you go to favorites, it is a different empty state again. Great, so now we have the boilerplate for our empty state. So let's go ahead and copy this empty search and paste it here. And let's rename this one to empty favorites. Like that.
Let's go inside of empty favorites and rename it here. And in here I'm gonna import, I'm gonna use the source empty favorites. Like that. And we're gonna change the text to say no favorite boards. And we're gonna say try favoriting a board.
Go back to the board list and in this case if there is a query of favorites use the empty favorites component like this. So we have empty favorites and empty search. So if you try now and click on favorites, there we go, no favorite boards. But if you try searching for something, it's going to say no results found. So the last one we need is this one and this one is also going to include a button to create the first board.
So let's go ahead and copy the favorites or the search and call it empty, let's call this empty boards like that. Let's rename this to empty boards. There we go. And then what we're gonna do is we're gonna use the note.svg like that. And let's give this a height of 110 and a width of 110 because it's gonna be a bit smaller.
And let's go ahead and give an h2 element of create your first board. And let's go ahead and give an h2 element of create your first board and let's go ahead and give this a paragraph start by creating a board for your organization and below that we're gonna add a div with the class name MarginTop6 and inside we're gonna add a button component from Components UI buttons so just make sure you import that and in here I'm gonna give it a createBoardText and a size of large, that's all it's gonna do for now And let me just go ahead inside of empty favorites here. And actually we don't have to do anything here. We can simply go inside of board list and then in this last if clause simply return empty boards from slash empty boards and there we go we now have three empty states. The first one is if we don't have any board at all so we're going to use this button to create a board later on.
We have empty favorites and we have an empty search. Beautiful! So we covered all of the cases and now we're finally ready to start creating our database schema and actually connect to the convex API. You're going to see how easy it is to do that. It's very productive to work with convex and we're going to finally start seeing some boards here.
Great, great job.