In this chapter, our goal is to continue where we left off with the categories and try and wrap them up. So that will include finalizing all the small details that we have around the drop-downs here. And that also includes the dynamic hiding and showing of categories depending on how wide the user's screen is. That will also include the view all button which is then going to open the sidebar where user can view all categories which did not fit on the screen. In order to make that a little bit easier to set up, I figured we should create a seed script for our categories.
And so that you can always just database fresh your database, right? So it starts from scratch, and then you have an easy way to just bring your categories back because you will most likely want to have the same categories, correct? So this is where kind of using bun will be crucial because this is a script that we're going to run as a standalone script. So it's much easier to do that with bun. I explained why in the beginning, right in my setup chapter here, zero config script running.
So it supports out of the box TypeScript and ES6. Nevertheless, if you for any reason have problems running BUN, and you fail to run the seed script, that's okay. It's not gonna block you with continuing with the tutorial. So if you, for whatever reason, fail at seeding the categories, don't worry at all. So let's go ahead and first confirm that we are on our main or master branch so just make sure you have merged all the changes from the previous chapter.
As always I'm going to do git status to see that I am properly aligned. Now what I'm going to do is I'm going to go inside of my source folder here, and I will simply create a seed.ts file like this. And now what I have to do here is I have to create a constant categories like this. And inside, I have to fill all the categories that I want. So this can be the simplest one, like all with a slug of all, for example.
I mean, technically, I don't think you would really put all as a category, but you know, if you want to do it, you can. So I have prepared for you a public guest, which you can visit. You can find the link in the description. Or if you have access to the source code, you can just go inside of the seed file, and then you will find the categories here, right? Because it doesn't make sense for me to write all of this.
So I'm just going to copy this entire categories file. And I'm just going to paste that here. And I'm actually going to remove the all, right? I don't like having that. Okay, we can leave it for now actually, but later I will think of a better solution to display all categories.
Great. So now we have an unused categories constant. As you can see, we have some main categories like business and money, and then we have subcategories. So that's exactly what we want to do. And each main category also has a unique color, so we can differentiate between each category.
So now what I'm going to do is I'm going to import getPayload from payload and config from atPayload-config. And now I'm going to go ahead and create an actual seed script. So const seed will be an asynchronous method. Let's define the payload using await. Await get payload with a config like that.
And now what we have to do is we have to go over each category and create a subcategory if it exists. So for const category of categories, const parent category will be await payload create, collection will be categories, and data here will have a name of category.name, it will have a slug of category.slug, color of category.color, and it will have a parent of null because these are parent categories. Now what we're going to do is for const subcategory of category.subcategories, or an empty array if there's none. And then we're going to go await payload create collection categories again with the data of name subcategory dot name slug subcategory dot slug and parent parent category ID. There we go.
So just like that, we've created a nice little seed script here. And now what we're going to do is just await seed and process exit zero, like this. So that's my seed script. All that's important is that you define your categories here. So this is the format, right?
For main categories, they need to have a name and slug. And then you put subcategories inside of an array like this, and they just have name and slug. And you can also add a color to the main categories. You don't have to, but you can. All right, so we have this seed script.
And now what we have to do is we have to add it to our package.json here, right? So I'm going to go ahead and add database seed. And I'm going to do bun run source seed.ts, like that. Or you can run this directly inside of here like this so what I'm going to do first is I'm going to do DB fresh so basically I want my entire database to be cleared. So I will get prompted now.
There we go. I say yes. This error is fine because we have no migration files because we are using MongoDB. And now let's do bun run database seed like this. So if no errors appear, I think we've done everything correctly.
There we go. No errors have appeared and it was successfully shut down after that. Perhaps I should have added this in try-cache maybe? I don't know. Let's see.
Bonn Run Dev. If yours didn't work, it could be because of this part maybe. Or maybe it's fine. I don't know. Mine seems to be just fine.
As you can see, I have all categories here. But you can see that we are running into some issues. Even our drop down position here gets confused. That's because this part is not supposed to be showing. So that's what we're going to be working on.
We're going to be working on ensuring that it only loads up to here. Because everything else, you can see, gets cut out. So it's just weird, right? So our calculation script works great, but maybe even this part shouldn't really show because you can see it's kind of cut off in this corner. So we are going to solve that by loading categories up to here.
And then we are simply going to add an additional last button here called View All. And that View All button is going to open a sidebar showcasing the user every single category they can have. Great. And if you want to improve your seed script, I think you can do this, like try await seed, and then console.log seeding completed successfully, and then exit with 0. And if you catch an error, log that error and exit with error code.
I think this can kind of improve it. And I'm going to add one more script here, which I will be using a lot. Database reset. So this will not be any payload reset script, it's simply going to do bun run database fresh, and then it's going to do bun run database seed. Now, I want to point out something important about our database commands here.
In the previous segment, we created this command, database reset, which simultaneously calls the database fresh, and immediately after that, it calls database seed. And while this might seem convenient, it can actually cause issues. And I no longer recommend this approach. The reason I don't recommend it is because I'm actually recording this in my chapter 15 of this tutorial. I ran this script right here and this is what happened.
As you can see, I started getting these errors for my categories, where it says that it has a write conflict, a transient transaction error. And initially, I did not get these errors, But looks like when I increased my seed script, which is something we're going to do in later chapters, I started getting these errors, write conflict. Now, write conflict by default means that we are trying to create something that already exists in the database in regards to a unique field. And then I realized, when we run database seed, there hasn't been enough time passed in between these two scripts, right? So basically this happens because we are trying to seed the database immediately after dropping and recreating all the tables.
But MongoDB needs a moment to settle after the migration and our end-to-end operator jumps straight into seeding. So instead, I no longer recommend using this at all. Instead, just use these independently. You can see what happened here. So I ran bun run database reset, and I got an error here.
It said that my categories are conflicting. After that, I went ahead and removed the categories from my script because I thought that the categories were problematic. But then you can see that in here, it's actually telling me something else, right? You don't even know what this is. This is in chapter 15.
This is my tenant. And then I realized, oh, so the script must not be working. And then I first tried manually clearing the database and then running database seed. And would you look at that? Seeding completed successfully.
And then I brought back my categories and I tried the same thing again and again it succeeded. So just to wrap it up, I no longer recommend using this at all. Go ahead and remove it. And every time you see me using it, use this, wait a few seconds, and then use this instead. Seed.
As simple as that. So then whenever I want to, I can just do bun run database reset. And this will first clear the database, right? So I have to say yes. And then immediately it runs the seed script.
And now it should add a console log. There we go. Seeding completed successfully. So I think that's a cool way so that you can always, you know, quickly reset your entire database, but keep the categories intact because you are almost always going to need them to try anything, right? So I'm going to refresh this, just confirm that everything here is okay.
And now we're going to go back inside of the CategoryDropdown component. Everything is fine. Great. So this is what I don't like. I don't like that when I hover over this, and then I go down, it disappears from its hover state.
So let's go ahead and resolve that. Let's go inside of CategoryDropdownComponent. So CategoryDropdownComponent is inside of Search-Filters here, CategoryDropdown. And this is where we can do that. So find the button with the variant elevated, which renders the category name.
And what we are going to do is we're going to add if is open. In that case, add background white and border primary, like this. So now you can see that it doesn't lose the selected state. Now depending on how you want it to behave, you can also keep its elevated state. You can see that it loses the elevated state, but at least now the border stays.
Previously, not even the border stayed. So it just looked weird Because it disappears. So what you can do is go inside of the button, find the elevated hover, and then just copy this shadow and translate. Let me just copy the entire thing. You don't need the transition.
So just copy this shadow. And you can just add all of that here and just remove the hover prefix on these three items. If you want to, you don't have to. And now you can see how it stays elevated while the submenu is open. If you prefer it that way, you can do it that way.
So that was really bugging me. I really wanted to fix that. Great. So the second thing I think I put inside of our itinerary here, all right, we see the categories, is the result of TypeScript issues, at least temporarily. I just think this is very annoying to have these errors.
So what I figured we should do is go inside of home and create types.ts. Again, we are going to organize this much, much better than what it is at the moment, but not the time for it right now. So export, and let's call it custom export type custom category, maybe like that. And this will be category from payload types. And then subcategories, which will be another type of category, but an array.
All right, I think if I hover over, let's see, how does that look like? I think this should be fine. Let's go ahead and try using that somewhere. So inside of my search filters index, I accept this. Let's say, can I do a custom category?
And then an array of those, basically. So if I see where I render my search filters, looks like it is working. So what I can do is I can also set the formatted data to have the custom category and an array of that. And I think that if I gave it something random like test A, yeah, then it throws an error. So it looks like our custom category was correct in this case.
So make sure you import custom category from ./.types. This is how it's supposed to look like. And then simply add it as a type here for the formatted data. And you should have no errors with this, right? That's because subcategories can be undefined.
And we make sure that we map this as category. If you remove this, I think everything will start throwing errors. OK. And let me just see. So now this is OK.
So no errors here. But we have to do the same thing for the categories here. So custom category and an array from dot dot slash types. I'm going to remove the console log. And it appears we are no longer getting the errors, but we are getting it here.
So inside of the category dropdown, change this to custom category from dot dot slash types. And we're now going to start seeing. OK, so category. No, we didn't have to map it here. There we go.
So remove that type here, and you can remove this import. So that part is solved. And now we have the custom category here. Let's see what the error is here. So we don't log in this.
There we go. I think that solved all type errors because it looks like subcategories. Why does it think that it still has docs? Right, because that's not our type exactly. Category, what's our category type?
So category and subcategory. But what we have to do here, I think, is category, maybe omit like this. Let me check if that will maybe improve it. OK, I'm not sure, but I'm not getting any errors. So I'm just going to leave it like this for now, or maybe can it be like self in inferring, what does that do?
Okay, now that causes problems. Okay, yeah, I'm not gonna do anything more. Okay, so I will just go back here, instead of my custom types. And I'm just going to bring this to category. That's it.
I'm not getting any errors. If you are, it's fine. It's completely fine, because we are going to use TypeScript later on. So we're just going to infer whatever the server gives us. We're not going to have to do all this specific types.
We are not going to be fetching like this in the layout. We're going to have a much nicer data access layer later on. But for now, I just wanted to get rid of the errors in the search filters and in the categories so that we can focus on developing inside of this area. So the goal for now is to make this responsive. So hide categories which do not fit.
At the moment, that's not happening. I can scroll all the way here, and I can see that I have some categories which clearly do not fit here. So let's go ahead and do that. We are going to have to do some things here. So instead of categories, let's do const container ref to be used ref from React.
And let's give it a type of HTML div element and null as default. We can copy and paste this and call this one measure ref. Then below that, view all ref, like this. Then we're going to add some states here. Visible count, set visible count.
Use state here and this will be data that length like this then in here let's do is any hover set is any hover use state make sure you import this from React, false by default. And then we're going to have is sidebar open and set is sidebar open, like this. Okay. And now what I want to do is add a use effect here. Make sure you import this from React.
So use effect. There we go. And I will just do one thing here. So I'm, at the moment, just going to mock the active category to be all, like this. So I will just assume this category like this.
And let's also do one more thing. Oh, yeah, we have to mark the categories as useClient. UseClient, OK. So now we should no longer have any errors. Let's just order them correctly.
I want all to be in the first place. And at the moment, one way we can do that is find where we load them. And we can add, I think an order. Direction. Let me just check how it's done.
It's sort. And we can just put name because all will be first that way. Later on, I'm thinking of just hard coding the category called all, right? But at the moment, it can stay like this. Okay, so make sure you add a sort name just so the category all is first, right, it just makes more sense that way.
Great, so now we have to fix this problem right here. And since we have the active category all, what we can do is we can go here and set is active to be active category, category.slug. And there we go. Now all will appear as the active category. As you can see, it's kind of circled, right?
The border of it is active. Okay. So now let's go inside of here and let's complete our use effect, but we just need to add a few more things. So const activeCategoryIndex will be categories, whoops, data.findIndex, category.slug equals active category. What am I doing now?
What I'm doing is I'm creating a logic which will tell me, is the current active category, maybe this one, right? Because if our current active category is a category that we have hidden away, a category which will be only visible once user clicks on view all, what I'm going to do is I'm going to highlight view all as the active category. So the user knows none of these categories are the active one. The category that you are in is too far out to be visible. So I'm going to give you a visual hint where you can find it, right?
Because it doesn't make sense that not a single category is selected when a user is obviously in a category. So is active category hidden will be if active category index is larger or equal than visible count. And if activeCategoryIndex is not minus one, meaning that it's not in part of the array at all. So now inside of the useEffect, let's do const calculate visible. So we are calculating visible categories here.
If we are missing the container ref, or if we are missing the measure ref, also make sure you add dot current in all of these places, like this. Or if we are missing view all ref current in any of these cases, we can break this function because there's no way for us to calculate anything here. So now let's go ahead and add some constants here. Container width will be container ref.current.offsetWidth. Then we're going to do viewAllWidth.
This viewAll is referring to the button which we're going to add later. Whoops. So we're going to ensure that the viewAllButton is always visible. And then availableWith will be containerWith minus viewAllWith. So we are ensuring that viewAll always has its space.
We don't ever want that button to be hidden. Now let's go ahead and do const items array from measureRef.current.children. Now if you're wondering what measureRef is, it's quite interesting. Actually, you're going to see in a moment what it is. It's a very unique solution to this problem.
So let's now do let total width to be zero, let visible to be zero, and then for const item of items, which we get from this measure ref, Let's get the width of the item using item.getBoundingClientRect.width. If total width plus the width of that item does not exceed available width, we're just gonna break the function. Otherwise, the total width will be increased by the width of that item, and we're going to increase our visible count. And then let's do set visible count to visible, like this. And now what we have to do is we have to add a resizeObserver.
So outside of that function, calculateVisible, make sure you're doing it outside, but still inside of the useEffect, const resizeObserver, new resizeObserver, calculateVisible. Effect const resizeObserver new resizeObserver calculate visible and then resizeObserver.observe containerRef.current. So that's what we're going to be looking at. And you can put an exclamation point here at the end because we know at this point it's going to exist. And very important, on unmount, make sure to disconnect the resize observer so we avoid any overflow.
And inside of here, let's see, do I need to add anything? Well, I think I need to add the length of the categories. My apologies, data.length here. I think I need to do that so that it changes every time we get a new category. If that happens, that's not supposed to happen, but if it does, it's going to recalculate everything.
Okay, so now we have that. And now we need to create this measureRef, right? If you're wondering what that is, measureRef will be almost a identical copy of this. So just copy it. But what we're going to do is we're going to hide it.
We're just going to use it to measure how long this will actually be. So let's go ahead and do the following here. Ref will be measureRef, like this. And you can add a little comment here so you understand. Hidden div to measure all items.
So we want to do that ahead of time, right? MeasureRef, last name here will be absolute, position, my apologies, opacity, zero, pointer events, none, and flex, right? So we don't want the user to see this. And the style here will include position fixed, top minus 9999, and left minus 9999. So we need to hide it like this.
We can't use display none or something because that will mess up the calculation. And I think this can stay exactly the same. I think this is just fine. And now in here, we can set visible items, right? So you know that these are the ones the user is actually going to see, but these are the ones we are using for our calculation.
We just need them to have the same amount. But something will be different for this visible items now. So let's go ahead and add some refs here. So this div is our container ref, like that. We already have the class name set.
Now what I want to do is add onMouseEnter here. The set is any hovered and set it to true. And onMouseExit, it's going to be false. OnMouseLeave, my apologies. Like that.
And then we're not just going to iterate over entire data. Instead, we're going to slice by the amount of visible count, starting from zero. And then, we can also do here is any Hubbard like this. And what we have to do now is we also have to add our view all button. And we are actually going to do that inside of the container ref.
So make sure you do it like here. This div will have a ref of view all ref and the class name of shrink0. And then inside of here, we're going to add a button. Make sure you import the button from components UI button. And this button will basically be a copy of what's inside of the category dropdown here.
So it's going to be this button here. So let me go ahead and just copy, how about, up to this part. We don't need the is open part because view all button, this one which we are developing, will not have any of those. So class name here, CN, and like this. So now I'm just going to go ahead and improve this.
So let me just collapse this like this. Okay, so instead of is active, this will be is active category hidden. And is any hovered here. The rest I think can stay the same and we have to import CN from libutil. So yeah, height 11, px4, background transparent, border transparent, rounded, full, hover, background white, border primary, text black.
And then if any active category is hidden, we're just going to highlight this category as the active one, right? Because this is the view all button. And we're also going to add a list filter icon from Lucid React and give it a class name of ML2. So make sure you have imported this and CN from libutils. And I think that already we might actually be ready to try this out.
The only unused things are this, the sidebar open, because we don't have it yet. And you can see, for a second, it kind of loads them there. But the moment it's over, you can see how it doesn't anymore. So there we go. You can see how this works.
It calculates based on the amount of space we have, even though you don't really have to always assume that the user will be doing this. This is a very, very edge case. I think that some original websites where I was looking at for this dynamic navigation menu don't even fix it if the user manually changes things. They only account for the initial load. And I think this is a very, very cool result.
So I know that we spent a lot of time building this. But that's kind of the point of my tutorials. I like to focus on details like this. I want to make it exactly as I've seen in these other websites. And I want to explore which possible ways did they do it.
It would be easier for me to just give you the simplest possible drop-downs here, but it wouldn't be as nice, it wouldn't be as cool. Now that we have this, what we have to do is we have to create the sidebar which will open up and display all the categories which were hidden. So that's what we're going to be doing with this. So let's start by finding the button here, the View All button. And on click, what it's going to do is set this to true, like this.
And when this is true, what we are going to be able to do is render the category's sidebar. So I'm going to render the category's sidebar right here in the beginning. Category's sidebar. Actually, no need for a comment here. It's going to be very descriptive itself.
So categories sidebar. And passing the open is sidebar open. And passing the on open change to be set is sidebar open. Obviously this does not exist yet. So right now, it's just an error.
So let's go ahead now. And we can do it inside of search filters. Let's go ahead and create the categories sidebar.dsx, like this. Let's export const categories sidebar and let's return a div categories sidebar. Now let's go back inside of categories and we can import the categories sidebar.
We are now only getting the type errors. Just confirm that you have the import here. So now I'm gonna go ahead and create a interface props here to accept a open Boolean and an open change, which accepts the open Boolean. Like this. Let's make sure to assign these props.
And by now, we should get rid of the errors in the other file. There we go. So now, what I want to do here, okay, this is how we're going to do it now. I'm going to accept my categories here in a form of data. So what I'm going to do here is I'm just going to put the custom category here like this.
Later, that's not how it's gonna work. You're gonna see why, because we are gonna be using this category sidebar in some different places. So it will be better for category sidebar to independently fetch the categories, which is not something that it can do now because it's a client component and we haven't really set up any client-side fetchers. So it's easier for me to just demonstrate like this now to do remove this later. So let's go ahead and do this.
Now we have the data, we have all the categories we need here. So what we're going to do is the following. Let's import everything we need from the sheet component here, just so we can see it actually open like this. Let's also add everything we need from the scroll area, from components UI scroll area. And now let's go ahead and actually turn this into a proper component.
So sheet with an open on open change. Now let's add the content here. Let's give some props here. Side will be left. Class name will be padding 0 and transition none.
And style here will be background color for now white, but later it's gonna be dynamic. Now let's add the header and let's give the header a class name of padding for and border bottom. Let's add a actually no need for a div. We can just do sheet title here. And inside, we can just say categories.
And you can remove this here. I think just by itself, this should be enough. We can also now pass the data here, as in the categories. And I think that if you click View All now, it will open up right here. There we go.
Perfect. So now what we have to do is we have to display all the categories, but with a twist because categories can have subcategories. So we have to find a nice way that when a user clicks on a category, it will display all the children items if they exist. Otherwise, it will just redirect. So that's what we have to do inside of here.
So we're going to use that data for that. So what we can do now is we can create a few states here. Let's call this parent categories, set parent categories, use state from React and pass in... Let's put null in the beginning like this. And we're going to give it an option of custom category or now like this.
Then you can copy and paste that and you can put selected category here and set selected category and this will be an individual custom category or now Both will be null in the beginning. So let's also call this, OK, let's keep calling it data. I like that name, actually. So Let's add a comment. If we have parent categories, show those.
Otherwise, show root categories, like this. So const currentCategories will check if we have parent categories. If we don't, it will use the data. Otherwise, we have to fall back to an empty array like that. Okay, and now what we're going to do is we're going to add outside of the sheet header let's add scroll area which we have imported and let's give it a class name of flex flex column overflow y auto height pool and padding bottom of two and inside you will check if we have parent categories.
If we do have parent categories, we're gonna add a native button element with the chevron left icon from Lucid React and a class name, size four, MR of two, and the text back. So if we have entered a submenu, we can go back this way, right? And let's go ahead and do an on click for now, just an empty arrow function and the class name. Pool width, text left, padding 4, hover, background black, hover, text white, flex, items center, text base, and font medium. Like this.
So by default this will now not show anything, But if you change this to true, you will have a back button like this. So you can go back. Great. So we now have this. And now, let's go ahead here.
And let's render the current categories like this dot map and get the individual category. So current categories are this, right? Either parent categories or the root data which we have. And in here you're going to render the button, again, native button. Put the key to be category.slug because each slug should be unique.
And for the class name, it's exactly the same as the one above. So you can just paste it here. Like this. And the only thing we're going to change is we're going to add justifyBetween after flex. Because it's going to have two items inside.
The first one will be the category name and then the second one will check if we have category subcategories and category subcategories at length is larger than zero. In that case, add Chevron right icon from Lucid React as an indicator like, okay, you can go further here. There we go. You can see that all doesn't have a Chevron, whereas business and money has a chevron. So that's what we're going to do here now.
If you want, you can also add a cursor pointer to these buttons, depending on if you prefer that way, like this. So this now loads all categories, as you can see. And now we have to do it so that when you click on a category, it either loads the subcategories, or if it doesn't, it will just redirect to a category. So in order to do that, we have to create a non-click here, which is going to be handle, handle, click, handle, category, click, that's a better name, and passing the entire category, which we are currently iterating over. And then we can go ahead and develop that.
Let's do it here. Const handle category click will accept the category which is a type of custom category. And then we're going to check if category subcategories and category subcategories length is larger than zero. In that case, set parent category. My apologies, set parent categories will now become the subcategories right like this.
And you can put here as custom category, like that. And setSelectedCategory here will be category. So the reason we have to put as custom category is because it thinks that subcategories have the docs and the next page and the total docs, so it's expecting this, basically. But we know that's not the case because of our formatted data. But I'm still not inferring the types correctly here.
So you can just put as custom category and then an array of those. There we go. And now we can do else here, which is basically going to do a couple of things here. So we just have to prepare the router from next navigation, not next router. And also let's move all of this to the top like this so basically what it's going to do is it's going to check for a couple of things so first this is a leaf category no subcategories.
If parent categories and selected category. This is a subcategory. Navigate to slash category slash subcategory, because that's how our structure will look like. So router.push, selectedCategory.slug slash category.slug, like this. Else, this is a main category.
Navigate to slash category like that. If category.slug is all, we're not going to navigate literally to all. We're just going to navigate back to the root page. Else, we are going to go ahead and navigate to slash category slot. So we are making an exception for all.
And after that happens, so this is inside of, Yeah, in this else, basically. So make sure you do it here. We are also going to handle open change false. So you don't want to do it... We also have a develop handle open change, I think.
Yeah. So you don't want to accidentally put it here. You want to put it in here. So only close the sidebar when we are redirecting, not when we just change the category. So let's develop the handle open change here.
The handle open change will, of course, accept the value if we want to open or close, but it will also reset everything. So set parent categories. And then finally, on open change, pass in open. So that's how that is going to work. And you also want to use this handle open change in the actual open change here, like this.
So now when I click here, there we go. The back doesn't work yet, But you can see how it opened all the subcategories, right? And you can see how when I close it, it resets. So I can now visit the photography. But if I click on something like other, it will redirect the slash other, which at the moment doesn't work yet.
So now what we have to do is that when I click on a subcategory, I'm expecting this color to appear, right? So we can do that quite easily. Const background color will be selected category, question mark, color, or white. And then we can just use that here and now you should have a much nicer experience there we go this looks really cool now let's fix the back button here because currently the back button is not doing anything here. So I'm just going to quickly develop it.
Const handle back click. If we have parent categories, set parent categories to null, Set selected category to null. There we go. That's it for the button. Now we can find this and just set it here for the back button.
There we go. So now you should be able to go back and you can now go here and your URL should be slash photography slash nature. Let me show you. So you should be slash photography slash nature when you click on a subcategory, right? There we go.
So I'm very, very satisfied with how this looks like at the moment. So we are still not completely finished. We are missing the individual redirects when I click on a category. And we're also missing the fact that this is not how I will be displaying categories on mobile. I mean, if you want to, if this is okay with you, you can do this.
But I mean, this is obviously not meant for mobile here. And you can improve it a little bit. If you remember CodeRabbit from our previous pull request, we had this pull request here, search filters. It actually recommended something. Let me find it.
The accessibility here. So maybe we could do something like that. Yes. So besides on mouse enter, we could also do on key down, or maybe on press. I don't know.
Yeah, maybe adding an on click, which will toggle the drop down as well. Let's see if that may be an improvement. I think it's definitely an improvement. So my category dropdown here is the focus. Right now, you can only open the category dropdown if you do on mouse enter or on mouse leave.
So how about we add on click here as well? I think that it's a good idea, actually. So let's add toggle dropdown. Let me just remove this pluses. Toggle drop down and I add it here.
So now when I click on it, if it has the subcategories docs, then it's going to toggle opening it or closing it. So I think this might be... I think nothing will change for desktop mode because you are still using on mouse leave, right? So nothing happens, but I think the mobile mode is a little bit improved actually now. But let me see.
Yeah, when I click I'm expecting it to like close and it does close but it doesn't work exactly perfectly. Yeah, if you want to you can add this, I don't know, you can maybe comment it out, you know, and just mentioned, potentially improve mobile. It's a good suggestion, but here's what we're going to do instead. We are going to hide this entirely on mobile. But just before we do that, I want to add the links.
So inside of category drop down, around the category.name, around this button here, let's do a link from next link. And let's go ahead and do an href here, slash category.slug like this. But check, if category.slug is all, In that case, we're just going to not add anything to the slash, otherwise, category.slug. So that's for the root pages. So go back to desktop mode now.
So if you click on education, you should get redirected to slash education now. And if you want to improve how this feels, you can also add a prefetch here, which will help us later on. But I'm not going to do that now. I would rather go throughout our code later so we can see what we should prefetch and what we shouldn't. And we also have to go inside of the subcategory menu here and do a similar thing.
Oh yes, so change this. So you can go inside of the subcategory menu and change this to custom category and remove the category type, I think. Do I use the category type anywhere? So we can do, you can leave it like this, actually. It's OK.
Yeah. And this error should now go away. And we are missing the proper link here. So the proper href here will be slash category dot slug and then slash subcategory dot slug. There we go.
So now if you go back here And if you go inside of music and click music theory, your URL should look like this, music and then slash music theory. There we go. Great, so we resolved that. One thing that is left is to hide this if we are on mobile. So one way that we can do this is by going inside of our search filters.
So go inside of search filters, index.tsx, and you can go ahead and wrap the categories inside of hidden lg block, like this. There we go. So now it's not visible on mobile at all. Okay, so it's not visible on mobile at all, but we are missing a button, which I want to put next to the search input here. So that's why I've mentioned that right now, I think that the category sidebar should fetch its own data because In order for it to populate now, what I have to do is pass the data here in the search input so we can do that now.
So pass the data inside of the search input. Data will be custom category and an array. Just make sure you added an import for the custom category. And then inside of here, you have to render the categories sidebar again. From .slash category sidebar, like this, passing the data.
And you also have to create the is sidebar open here. So const is sidebar open. Set is sidebar open. Use state from React with default false. Now let's go ahead.
Oh, wait. Why is my? OK, so now I'm going to, first, I have to add the data here. OK, that was the issue. And now open is sidebar open.
On open change, set is sidebar open. OK, and now we have this little to do here, add categories view all button. So add a button from components UI button. And inside of here, you're going to give it a variant of elevated. You're going to give it a class name of size 12, shrink 0, and flex on mobile, but on large hidden because on large we have all the categories we need.
And in here just the set is sidebar open to true. And the button list filter icon from Lucid React. Make sure you import list filter icon from Lucid React. And I think if my input size is height 12, yeah, then this will match nicely. And I think I need to mark.
I'm going to mark the search input as use client here. So I can use those. And now, There we go. There we go. Now, as you can see, I can click on the sidebar here, and I can load my data.
So depending... Later, we're going to add the RPC, so it's going to be much easier for this category sidebar to independently load its own categories. Because right now, we are prop drilling all the way from the layout file, right? We are prop drilling our format to data, to the search filters, to the search input, to the category sidebar. It would be nicer if category sidebar could just load it themselves, right?
Okay, so I think I'm gonna end the chapter here, but I just want to check if that was all that I intended here. We created this temporarily fixed with the custom category, And we did this, definitely. And we even added all the links, the stuff. Great, so what we have to do now is we have to push the GitHub. And then we can review everything that we did.
So I'm gonna shut this down and do git checkout-b. What's my chapter name? 06 categories finalization. Like this. Git add, git commit, 06 categories finalization, and then git push uorigin06 categories finalization, like this.
So now you should see that I am on a new branch here. You can see in my graph that I have detached from master or main. I'm no longer there. So I have to go and create a new pull request here. There we go.
Again, you can click on new pull request here and then just find this and then click create pull request. I'm going to create the pull request and then let's see what our one reviewer will tell us about our changes. You can of course already merge since you probably don't have that at the moment. And here we go. Summary by CodeRabbit.
New features. We introduced an interactive sidebar for category selection with navigation and a back button. Enhanced category display with a responsive layout that adapts to the number of visible categories and adds a view all button. So that is a very cool thing it noticed. We've also updated the search interface to integrate sidebar toggling for smoother use on mobile devices and we improved the link navigation.
That is a perfect summary of what we did. It almost matches what we wrote here, right? And let's see what else it says here. So it gives us a walkthrough. So it noticed the database seed and the database reset file, the new types here, the custom category.
And in here we have sequence diagrams for how our database fresh and database reset works, but also how our categories sidebar works as well. And it also has some nitpick comments. It's nice that it hides them here because yeah, they aren't exactly crucial, but it's like some very small suggestions, like removing this or maybe removing the question mark since we don't need it now. I think it's really cool, right? It even added, hey, maybe you should check if the category exists before you seed the category.
:12 So really cool, right? Great, I'm super satisfied with the result, so I'm going to merge my pull request. And that marks the end of the branch 06 categories finalization. You'll be able to find it here. And now, as always, we have to go back to our main or master branch.
:35 Again, you can either use the dash or write git checkout main or master. And then git pull origin, again, main or master, depending on what you are using. And git status to confirm that everything is up to date. And in here you should see that we detached and then we merged and now we are back on master branch. If you want to you can run your app just to confirm everything is still working fine.
:03 But I am pretty confident this is correct. So we mark push to GitHub as complete. There we go. This pretty much finalized the categories. We might go back here later when we implement some proper sub-routes and things just to display the breadcrumbs, but it's going to be minimal changes.
:24 And I think that this was the hardest UI-wise thing that we're going to build, right? So UI-wise, front-end-wise, this is the hardest this tutorial gets. Everything from now on will be easier than this. For the back end, I'm not sure I can guarantee that. We have a lot of things to build there, but for the front end, I'm pretty certain that this was the most challenging thing to build.
:48 Amazing, amazing job and see you in the next chapter.