In this chapter, we're going to go ahead and develop the basic search filters. So the search filters will include this input component right here, and the category drop-down menu below it. And you can see that when we hover on a specific category, we're going to open a sub menu, rendering all the subcategories of a specific parent category. That being said, this entire component is more complex than it seems. Primarily because we're going to develop it from scratch.
So we're not going to be using any chat CN dropdown, even though it has dropdowns. I just found it that in order to make it look like this and behave the way I wanted to behave It is better for us to build it from scratch This also includes one important functionality which you are going to see when it comes to responsiveness of this component. Because in this view all button, you will be able to see all the categories which did not fit onto the screen, which is more challenging to develop than it sounds. But for this chapter, the goal is very simple. I just want to display all categories.
I don't care about responsiveness at this point. I just want to display all categories and when we hover on a category I want to display its subcategories. In order to do that we first have to enhance our categories collection, meaning that we have to add fields such as subcategories, a color, because each of our main categories will have its own background color, a slug for each category, which you can think of as a unique ID for each category, and some other fields which will help us display this in the best way possible. And then we're going to go into the actual search filters component. And I've already explained the goals there.
So in order to get started, please confirm that you are on your main or master branch. Here's my graph to help you understand where we are. So we are back in the master branch after our last chapter Payload Integration. As always, I'm doing git status to confirm I'm in my master branch and everything is fine. Or your main branch, depending on what you use.
So Let's go ahead and do bun run dev, and let's go inside of our source collections categories. And we are now gonna go ahead and enhance this. So the first thing I'm going to do is add another field with the name of slug which will also be a type of text. It will also be required but also is going to be unique. And since we are going to query by this field we are also going to set an index fill to it as well.
Now we're going to go ahead and set the name, color, and the type of text. And it's not going to be required, but we will allow super admin to change the color of category if needed. And now we have to do the actual relation between a category and the subcategory. So since they are technically the same collection, what we ought to do is give each category a parent, right? Which is a type of relationship.
And it has a relation to itself. And we are going to limit hasMany to false. And important thing is it's not going to be required. So if a category doesn't have a parent, that's how we are going to know that it's a parent category itself. And we are also going to add subcategories, which will be a type of join with a collection of categories themselves on an individual parent.
So this has to refer to this right here. And this will be many. And again, not required. You can, of course, learn more about these if you go inside of fields here. Let me go ahead and find the fields.
For example, you can explore what is a relationship field. And you can try and you can go ahead and read this to understand it a little bit deeper if you're interested. Same with the join field. The documentation is excellent, right? You can definitely learn a lot from it here.
I personally prefer, and I always did, learn by writing code. So that's how I'm gonna explain this through as well. So let's continue with this. That's actually it for our categories collection. So if I go to localhost 3000, and if I go to my admin here, I should now see some new options.
So I think I used admin-demo.com and demo here. There we go. And I'm gonna show you one quick trick for something unrelated. But there we go, you can see that my categories here have no slug and no color set. So basically, what I got reminded of is, what if you forget your password, right?
Let's say I, where is my logout? I think it's here. There we go. So I log out. What if I forget my password?
It's asking me for my email, but we entered the fake email. So What you can do in that case is, let me try and find, reset, migrations. There we go. So you can reset or you can do fresh, drops all entities from the database and reruns all migrations. And this will actually come in quite handy, especially if you mess something up.
So add database fresh. And you can just do this like that. So then whenever you have invalid data or you just want to start again, you can just do bun run database fresh. And this will prompt you if you want to drop your database. So there we go, we just dropped our database.
So if you do bun run dev again, and go to localhost 3000 slash admin, it will behave as if you never had an account in the first place. And there we go, it prompts you to create a user. So go ahead and create the user and let's create our category now. So I'm gonna go ahead and create software development now like this with a slug software dash development. And for the color, well, it can be anything you want.
But for example, let's do something like this. And yes, you can create your own component, which is an actual color picker. But for simplicity's sake, we're just going to keep it like this. Let me just add a little brighter color, something like this. So this should be like a yellow color.
And as you can see for the parent, I currently have no options, right? I can only save this, and this is now a main parent category. And it should be visible on the localhost of 3000 now because we do load them right here. There we go. So this is the color here and the name is software development.
So now what I want to do is I want to add a subcategory to it. So let's go ahead inside of here and we can basically do it in many ways. I think that we can go in here, subcategories, and we can click add new. You can see this button here, add new. So you can click here and let's, for example, call this frontend develop or just frontend and just call it frontend.
And for the color, we don't actually have to put anything here because subcategories don't have to have colors. The parent has to have color, like this. And you can just click save. And there we go. Now you can see how my software development main category, who doesn't have a parent, has its subcategories.
So now I am personally going to limit my categories to just two levels, right? The first level will be the parents and the second level will be the subcategories. But I am not going to allow subcategories to have their own subcategories. But you might have noticed, if you click the edit button here, it actually allows me to add this even further. There are ways to validate that, right?
So you can go ahead and go through here and search for validation of each fields. So for example, you can add validate here and do your own logic. You can query the database in here or do whatever you want to tell the user why they can or cannot have a specific field, right? So I could technically add validate here and then do if a subcategory don't allow, right? Something like that.
But in my case, the only person who will be able to edit the categories is a super admin, meaning someone who developed the website, someone who knows how categories work. So I don't really have to worry about some other user creating this and then having invalid data structure. So in my case, this is enough because I know how I will personally handle this. In your case, you might choose to not even have subcategories. But this is basically what I want you to have.
I want you to have one main category, and then I want you to have one subcategory which has a parent. I think that is a good start. So there we go. We now have one main category here. And if you look at subcategories here, you will find repeated documents, right?
And inside of here, you can find the ID. So don't worry, we are gonna display this in a nicer way. It's kind of hard to read it like this. So let's go inside of our page, inside of app, home, instead of app, app, home, page. And I'm just going to do a console.log of data, just so we can see it in an easier way.
There we go. So we now have documents and here's a little bit of a problem. So both of them are now loading, which is technically fine. But what I would expect is in the first level of this matrix, I only want my parent categories to load. So what I can do here is add a where, and inside of here, I can check if parent exists false.
So what this is going to do is it's only going to load one document, software development. But good news is that it's also going to load the subcategories. As you can see, it loads the subcategories in its own documents here. And in here, we can find frontend. So that's what we want.
In the top-level matrix, I just want my big software development group. Nothing else. So only one item in the array. And then in the subcategories, I want the frontend to be loaded. And I just want to show you one more thing.
There is a thing called depth inside of the node API here. And if you set it to zero or one, you will get a different kind of data structure. So right now, everything looks the same so far. But when you take a look at the subcategories now, you can see that it did not populate the ID here. So what you have to do is you have to put depth to at least one or two.
Let me just confirm. There we go. So depth has to be at least one if you want to load, well, one level deep, I suppose. So zero basically means flat document, whereas one means load subcategory or maybe better populate subcategories like this. So I just prefer having this explicitly, rather than relying on the default.
I like to see exactly what I'm populating, right? And nothing deeper than that, right? This is exactly what I need. Great. So now what we have to get started with is the actual Search Filters component.
So we resolved this. And now Let's go ahead and develop the Search Filters component. So I'm going to go ahead in here. And let's go inside of Home here. So for now, we are going to stay and create the components here.
But the way I want to do it is by creating a folder called search-filters. So I'm just going to create a component because it's going to have a lot of moving parts so I want to keep them all at least in one folder. Again, don't worry, we are going to separate all of these components into their own modules, but we are going to do that in some other chapter for now. I just want to focus on establishing an MVP with payload and the categories. So inside of search filters, add an index.tsx component here.
Let's export const searchFilters. And inside of here, let's just add searchFilters like this. So just a very, very simple component. And now, we are going to render that inside of the layout.esx. So we are going to do it inside of this layout, right?
Inside of app folder, app route group, home, layout. Not the root layout, not this one. This layout, where we render the nav bar. And just below that, add search filters. And make sure to import search filters from .slash search filters.
I'm going to close this now. And there we go. Now you should see a text which says search filters. And now we're going to go ahead and do the following. So inside of this layout, I'm going to go ahead and mark this as an asynchronous method.
And I'm going to open my page here where I have the payload imports. Basically, I'm going to move that logic to here. And if you're wondering why am I not extracting this logic and reusing it somewhere, That's because this is not how we are going to fetch things in the future. We are going to be using trpc, but it's not the chapter to do that just yet. So for now, we are just using like a dirty quick way to get our data.
Great. So no longer need anything here. We can just put homepage here, remove asynchronous here and remove these two imports. That's it. Just make sure that your layout is asynchronous and make sure there is no use client in layout here.
And now you have the data here. So we're going to go ahead and pass in the, well, we can just pass in data here as well. Let's go inside of the search filters and create an interface search filters props, or just props, data, and you can set any for now. And inside of here, JSON, Stringify, data, null. So basically, you should be able to load your categories now and pass them to the search filters component.
So there we go. We just transferred that JSON to this part right here. Obviously, later on, we're going to have proper types. But for now, I just want to leave it like this inside of search filters let's go ahead and give the div a class name px4 on large px12 py8 border bottom flex flex column gap 4 and full width And if you have an error here, that's completely fine because later on it's going to look different. So now your search filters component should have some more space here.
And now we're going to go ahead and just above this add a search input component. Let's go ahead and inside of the search filters folder, let's create search-input.tsx. So that component is this one. Right, we're just going to add a simple input here before we go onto the categories. So let's go ahead and create an interface props.
The only props this is going to have is an optional disabled prop. And let's export const search input here, like this, and assign props, and the structure disabled. And inside of here, we're going to return a div with a class name flex-item-center-gap-to and width full. And I want you to add a div with a class name relative and full width. Let me just fix the typo in the class name.
And then we're going to render search icon with a class name. Well, let's just leave it like this so it's easier for you to understand what we're doing. So just add the search input and the search icon and the input here, the input from components UI input, the search icon from Lucid React. Go back to the index and import search input from ./.searchinput because they're in the same folder here, right? So let me go ahead and refresh.
And there we go, you should see a big input and a search icon here. So what we're going to do now is the following. I'm going to go ahead and give the input a class name of PL8 and the placeholder of Search Products. And disabled of disabled. Basically, we are passing this prop from here.
So now you should see a big text which says search products. But this icon is still not inside. So we're gonna give the search icon a class name of absolute, left three, top one and a half, minus translate, minus Y, one and a half, size of four, and text neutral 500. There we go. So this is how it's going to look like.
Like this. Let me just zoom this out to 100% just to see how it's looking. So the input looks a little bit too big. So let me just check what its height is. Let me try and see the height here.
It's 65 pixels. Do we have that in RAM? So this is height 12. How about we go inside of the input? Maybe it's because, oh, it's not P8, it's PL8.
My apologies. There we go. So this is how the input is supposed to look like. Now it looks a bit more normal. Okay, so what I'm just gonna add now is outside of here, I'm gonna add to do add.
This will be categories. You all button and to do add library button. So we don't have the materials to build this at the moment, so it doesn't make sense to build it because you can't even see exactly what this is going to be. And it's also only going to be visible this one will only be visible on mobile and this one only when the user is logged in so those are the states we don't have yet great so that's it for the search input and now we can go ahead and focus on the categories So I'm going to go ahead and develop the categories component like this and pass in the data, which in this case can just be data. So let's go ahead inside of search filters folder where we have the index and the search input and create categories.tsx.
Let's create an interface, categories props, and inside of here we're going to go ahead and do categories, any, my apologies, data, any. Let's export const categories, like this. And let's return a div categories. Again, this can just be called props. No need to be that descriptive.
And extract the data. And now inside of here, you can just import the categories from .slash categories, and you can remove the JSON stringification here like this. So let me go ahead and try and do I still do the JSON somewhere? There we go. So once I refresh you should just see a text categories.
Now, one thing that I want to do with this data that I have is format it in a specific way. So right Now, if we go ahead and console.log the data, I think you already know how it looks like, right? So let me try. There we go. It's basically docs, and then inside another subcategories docs, Right?
And this is great if we were to use pagination, but we are not going to use pagination for our categories. We are not going to have thousands of categories, right? It's going to be a normal amount. So two things I want to do here. First, pagination false.
I wanted to always load all of them. And now the second thing I want to do is the following. I want to go ahead and do const formatted data. Basically, what we're going to do is data documents dot map, and then get the individual document. And then inside of here, I'm going to spread the entire document and then get the subcategories and do the same thing.
So doc, subcategories, docs, like this. Now the problem is This is optional. So what we ought to do is this, like this, .map, and then just get the doc and spread the doc. I think this is how I imagined it. Let me just see.
Yes, you will see an issue at this point, and I'm gonna explain what's going on here. But I think that I did this correctly. So now let's have data and let's have formatted data here. So you can see the difference between what we originally fetch and what I transform it into here. If I did this correctly, let's see.
So we now have data, which is our good old docs. And then again, subcategories docs, basically a very long JSON, whereas our formatted data is just our main category, software development, and then subcategories, immediately the object. And then finally, nothing here. Great. So why is this happening?
Why is this warning me of an error? Here is why. Remember when I explained depth to you? Right? So, if I say depth is zero, in this case subcategories documents, every document would be a string because it would not populate them.
But if I change this to one, in that case, it becomes the category object. At the moment, Payload SDK cannot automatically infer the type based on depth. But then again, depending on when you're watching this video that might not be the case because they do have a pull request which features the depth generic and fully type safe result types for relations and joins. So that is this exact thing that we are talking about. If you specify a depth, it is normal to expect the type to be the proper object.
But in our case, we have several options here. Since we know that by adding depth one, we've added a console log, we've convinced ourselves with our own eyes that in that case, the document is populated, there are a couple of solutions. So you could do just doc, maybe like this, as category from payload types. All right, so let me just import this and then we'll see what we can do. So you can import category from at slash payload types.
So maybe not like this. Let's see, maybe like this. There we go. So if you do it like this, all of your problems are solved. And you can add a comment here explaining, because of depth one, we are confident doc will be a type of category.
So I think this is a good comment to have just for you to understand why you need this, right? Because they don't automatically infer this. So populate subcategories, subcategories, zero will be a type of category. There we go. So you can just add this little comment if it helps you understand what's going on.
Whereas if you change this to zero, I'm pretty confident the entire app at this point will maybe break. Let's see, formatted data. There we go. Yeah, you can see what happens. The subcategories have basically split the entire string ID to that.
But if you add depth one, just go ahead and confirm this yourself. If you add depth one, you should see subcategories as completely normal objects here. So that's quite important for you to have. And if you want to, you can also do subcategories here as undefined maybe, just so you don't have an additional array here. Let me check if that is correct.
So I have one subcategories, and there we go. Now in here, frontend doesn't have its own subcategories. So now our formatted data is exactly how I want it to be. So we can take that and we can pass that to the search filters. We don't have to change this because we already set it to any.
And now we can go ahead and render that here. So JSON stringify data null two. And this is now officially the data we can work with. There we go. So we just get an array of our main category and then its subcategories here featuring the name frontend.
There we go. So it took us half an hour, but we finally got to this point where we can build this component. So now what I want to do is I want to go ahead inside of this div and just go over categories and by apologies data dot map and then get the individual category like this. And inside of here, I'm going to add a div with a key of category.id. And if you want, you can do category, category from payload types.
There we go. So this is kind of a dirty way to have the types. Again, don't worry, later, we're going to use the RPC. So all of our types will be inferred throughout the app. Don't worry, this is not going to use any anys or tricks like this, it's going to have proper types.
So inside of here, let's add a category drop down. And let's pass in the category to be category like this is active for now will be false is navigation Harvard will be a prop that will be useful later, but for now we can just set it to false. So instead of search filters, now add category-dropdown.tsx. Let me show you how that folder looks like now. So search filters has categories, category drop-down, index, and search input.
Let's go inside of the category drop-down here. So interface props, it will have category, which can be a type of category here, is active, which can be an optional boolean, is navigation hovered, an optional boolean. Let's export this category, drop down. We can go ahead and destructure the props. Category is active is navigation hovered.
And now what we're gonna do here is finally render a button. So let's go ahead and import a button component here. And for now, let's just do category.name in here. Make sure you have imported components UI button here. Go back inside of your categories and import the category dropdown from ./.category-dropdown.
So now when I refresh this, I should be seeing a component here. So let me just check why is it not working. Category dropdown. Let's see. Console.log data test.
Just to confirm, is it loading here? Data test. It is. It most certainly is loading here so data dot map category drop-down return Data.map, category dropdown, return. What if I add variant elevated?
Maybe it's that. OK. Let's see. Maybe I'm missing something obvious and you have already seen it. I don't know.
Am I rendering everything else correctly? Index categories, I am rendering the categories. We have this here, category drop down. Okay, I'm just going to pause and see what I'm doing incorrectly. All right, I'm not rendering this correctly.
I'm using curly brackets. So that's not causing a render. My apologies. So now when I refresh, there we go. You can see a big button and software development.
So Let me just show you. So just a category dropdown is being rendered here. And inside of here, I added this console log while I was debugging. So I noticed that this never renders. And then I figured out, oh, it must be because, well, I never render it.
So I'm just going to remove this console log. And I added this variant elevated here. Everything else is exactly the same. Nothing has changed. So yeah, if you have the same mistake as I do, just fix using the proper brackets and now you should see the text software development here.
So let's go inside of the category drop down and let's go ahead and style this even further so it looks a bit better. I'm now going to add a class name here with the cnutil. You can import that from libutils like this and inside of here add a height 11 px 4 background transparent border transparent rounded full hover background white, hover border primary, text black, like this. So only when you hover will you actually see the effect happening? Like this.
And then I'm just gonna add a couple of things. So if is active, and if not, is navigation hovered? So this is some additional logic which will come in handy later. In that case, just add background white and border primary. So basically what this will mean, if I click on software development and software development becomes the active category, we are just gonna add these two additional styles to ensure that it has the same style as if it were hovered, right?
So that's what we are gonna be doing. Right now it doesn't make sense because we can't have active categories. Great, So what we have to do now is that when I hover over it, it needs to drop down and display the subcategories. And that's going to be the last thing we're going to do for this chapter because it's already long. But I do think we can do that here, even though it will take some time.
So the first thing I want to do is I want to add a state here. Const is open and set is open. Whoa, my apologies, I didn't see this. Const is open and set is open. Use state set to false.
And you can import this from react and mark this as use client otherwise you're going to get an error so just make sure you have use client in your category drop down then add a drop down ref from user ref give it the type of HTML div element and null. Make sure you have imported user ref from React. All right, and now do const on mouse enter. If the current category has subcategories, in that case, set is open will be set to true. So only we actually have something to show are we going to open the actual subcategory menu.
And do const on mouse leave set is open to false like this so now Let's go ahead and wrap this button in a div like this. And let's give this a class name of relative, a ref of dropdown menu of drop-down ref on mouse enter on mouse enter and on mouse leave on mouse leave like this so nothing much should change now but the state is open should change depending on if we are hovering over this or not. So we need to have it in JavaScript because it's just not enough for us to have it using the hover methods. So now I'm going to wrap this button again in a div. And I'm gonna go ahead and give this a class name of relative like this.
So just that. And then what I'm going to do is I'm gonna go outside of this button, but still inside of this class name relative. And I'm going to check if category subcategories, so if we have subcategories, and if category subcategories length is larger than zero, in that case, we're going to add a div here, which will actually be a self-closing div. And we're going to give it a class name, which is going to be dynamic using our CNUtil. And we're gonna do the following.
Opacity 0 by default. Absolute minus bottom 3. Width 0. Height 0. Order L 10 pixels.
If you're wondering what this will be, also you can ignore this error for now. What this is going to be is this, this little pointer. So that's what we're going to do now so you can see something happening when you hover over this. So opacity zero, absolute minus bottom three, width zero, height 0, border L 10 pixels, border R 10 pixels, border L transparent, border are transparent, border bottom black, left one and a half, minus translate, minus X one and a half. And then add a dynamic class if is open, in that case, Opacity 100.
So let me just refresh and see. I can't see it appearing, so I am gonna check if everything I did here is correct, starting with this. Hello. So I just want to ensure that this happens. Let me just see.
Something weird is happening now. Let me try expanding a bit. So it properly enters here. OK, that is correct. Let's see, category, subcategories, and if category subcategories length is larger than zero, then we render this.
Let me just confirm that I did this correctly. I think I forgot to add one thing, border bottom and pixels as well. So Border left, border right, and border bottom. And there we go. Now you can see it's starting to appear here.
So it's not perfect. It's a little bit glitchy. But it is appearing every time when I hover on something. Great. So let's just confirm we have this set relative, we have that set relative.
And I think that this should be OK for now, because we are missing another component that we need to have here, which will finally make this look like it is supposed to. So in order to do that, we first have to develop a useDropdownPosition hook. So basically, in order to make this work properly, we can't just show a drop-down below this because it won't exactly work on some smaller devices and it can be quite glitchy if you're not careful. So we have to develop a hook which will help us determine the best position for this drop-down to open into. So let's go ahead and let's go inside of...
Well, we can do it instead of search filters. Just create a hook called use-dropdown-position.ts. So use-dropdown-position. And inside of use-dropdown-position, export const use-dropdown-position here. Inside of here, we're going to add a ref, which will be ref object from React, HTML div element, or null, or like this, or it will be a ref object of HTML div element.
Whoops. Like this. And now we're going to develop a method getDropDownPosition like this. And that will be the only thing we're going to return from this hook, getDropDownPosition. So you can go back inside of the category drop-down here and we are just going to prepare that.
So here where you define the dropdown ref, you can just do useDropdownPosition and pass in the dropdown ref. You shouldn't get any errors here because we added the correct type. And now from here, you can extract getDropDownPosition. And now we are going to use that drop-down position to correctly render the submenu. So let's just go ahead and develop this here.
So we're going to start by checking if anything is missing. So if we don't have ref.current we're just gonna return the default top and left to be 0 0 right There's nothing for us to calculate here. Now let's go ahead and get the rect using ref current get bounding client rect. Let's define the drop down width. So this is how wide the drop down will be 240 pixels like this.
And the way we calculate this is basically width of drop down is going to use this class name w 60, which is equal to 15 rem, which is equal to 240 pixels. So that's how we get to this. So we are going to be using this class name on the subcategory menu later on, right, just in case you are wondering where did I get this magic number. So let's go ahead and calculate the initial position. So let left is going to be a rect.left plus window.scrollX.
Const top will be rect.bottom plus window.scrollY. Now we're going to check if drop-down will go off the right edge of the viewport. Basically, we want to make sure that our drop-down menu behaves correctly in all cases. So if left plus drop-down width is larger than window inner width, that means it's overflowing. So we're going to align to right edge of button instead.
So this is something that most of this drop-down libraries have built in, but in our case, we are building our own here. So let's do the left is now rect.right plus window scroll x minus drop-down width. We are basically preventing things from overflowing. So if after this, we are still off screen, aligned to the right edge of viewport with some padding. So if left is smaller than 0, in that case left will be window inner width minus drop down width and padding, which is 16, like a buffer here.
And we are close to finishing now. So one more check, ensure drop-down doesn't go off left edge. If left is smaller than zero, left is 16. Basically, some padding and return top and left. There we go, so let's go over this again.
So what's happening here, if the dropdown ref, which we passed, right, let's see, so this will be the dropdown ref, our div wrapping our button, that's the dropdown ref, that's what we are passing here. So If we can't detect that element, we are not even gonna bother trying to put the subcategory menu in a perfect position. We're just gonna return the default top left zero, right? So we are going to be using the getBoundingClientRectangle from ref current. We are defining the dropdown width because we obviously use it in a lot of calculation.
We know it's gonna be 240 pixels wide because, well, I told you that, and I'm gonna come back to this when we develop it in a second. And then we are basically calculating the initial position of the drop-down menu and we are just making sure that it doesn't overflow, right? We are making sure that no matter how user behaves, whether they do this or that, we're making sure they can always, it will basically move, it will be maybe here, maybe it will be here, depending on how the user is looking at it at any given moment, right? So that's what we're handling here. All right, so I think we've developed this correctly.
And now what we can do is we can go ahead outside of this div here and add subcategory menu and pass in the category. Category is open, is open, and position, drop down position. And drop down position will come from get the drop down position. And you And we can call that here. And this will be dropDownPosition.
There we go. So now, subcategory is not defined, of course, so we do have to define it. So let's quickly do that. Instead of search filters, add subcategory menu.tsx. And I'm going to go ahead and create an interface for this, but change this to be Any.
Or you can just do Category from Payload Types. So subcategory menu props like this, it will have category is open and position I will just change this to props let's export const subcategory menu Let's go ahead and use the props. And let's extract them. So category is open and position. And now inside of here, what we're going to do is check if we are not open or if there are no category subcategories, or if we do have category subcategories, but its length is zero, in that case, we are going to return null, right?
So we are only going to open the submenu if all of this match here. And let me just check this. So should I do it like this? Like that? It's not satisfied, no matter what I do.
I'm just going to leave it like this for now. And we're going to come back to it later because we know it works. We know that subcategories is an array. Oh, yeah, it's because we use formatted data, right? So it doesn't know, it's expecting us to access the docs first, right?
But we know that's not how it works now. So yeah, you can leave it like this for now. Later when we have TypeScript, it's gonna work differently. So now what we're going to do is const background color will be category.color or we're going to use f5 f5 f5 as our default color. And now inside of here we can return a div and the class name of fix and the z100.
And we can give it a style, top position, top and left position left. And now inside of here, we can add an invisible bridge to maintain hover. If you're wondering what that is, if you were to hover over business and money, this dropdown menu would open. And then if you were to hover in between these two lines, it would close. And that's pretty annoying.
So what I figured we would do is create an invisible bridge here, which will simply maintain that element's height. So that's what we're doing. So let's go ahead and create this invisible bridge here. So it's a self-closing div with a very simple class name height three and width 60. So this is what I was talking about here.
That's going to be the width of the drop down of the subcategory menu width 60. So now we can use another div here with the class name with 60, text black, rounded medium, overflow hidden, and border like this. And inside of here, what we can do for now is just say a paragraph subcategory menu. So let's see what we have for now. So all of these errors are completely fine.
We're going to resolve that later. So now, import subcategory menu and render it here. And let's refresh. Let's see if we have any errors. There we go.
You can see my subcategory is appearing here. But it looks like this little button is still stubborn. So I'm going to see how to resolve that as well. But let's just continue with the subcategory menu for now. So what I want to do is I want to go back here in this div with 60 text, black, rounded, medium, overflow, hidden border.
And let's give it a shadow of 4 pixels, underscore 4 pixels, 0 pixels, underscore 0 pixels, and then RGBA 0.0.01, like this. And then, minus... Make sure you add another... So let me show you how this is supposed to look like in one line. You can pause the screen and take a look.
All right, so make sure you close the square bracket and then minus translate minus x two pixels and minus translate minus y two pixels. So Let me just refresh. There we go. So now you can see it's starting to take some shape here. And now we're going to add the background color.
So I'm going to add style background color and it's basically going to be this constant. So it's going to read the color which we defined from the admin dashboard. It's looking pretty cool. And now what we're gonna do, don't worry, no more components. What we're gonna do now is add a div here, like this, and category, subcategories, map, subcategory.
You can give it a type of category. I think it's just going to make this a little bit easier. And add a link from next link. Give it a key of subcategory.slug. And in here, render subcategory.name.
And you can give this an href. For now just an empty string. Later we're gonna give it a proper href when I explain to you our routing logic. And add a class name here. Full width, text left, padding 4, on hover, background color black, hover text white, flex justify between, items center, underline font medium.
Like this. So now when you hover, there we go. You should see the only subcategory in here, like this. But there are a couple of more things that we have to do here. But this was basically what I wanted us to achieve.
I wanted us to load the main category and the way to load the subcategory. So I just wanted to find a way to properly position this carrot before we go on. So I'm gonna go back inside my categories here and see if there's something I have to do here. So I'm just gonna add the proper wrapping elements. For this outer div in my categories component, I'm going to give it a relative and a width of full.
I'm then going to go ahead and wrap this into another div. And I'm going to give this div a class name, flex, flex-novwrap, and items-center. And there we go. That resolved it. So I think it was this class name around each category drop down.
We basically needed a flex, flex-novwrap, and items-center. There we go. So you can see that now software development has a very nice front and sub menu. So now if you go to your admin, so localhost 3000 slash admin, go inside of categories. Let's try adding a subcategory.
So I'm going to go inside of my front end and I will add new. Oh, no, this is the incorrect one. My software development here and click Add New. And I'm going to add backend now. Backend.
And let me just find a nice little... Actually, no, backend doesn't need the color, my apologies. So I'm gonna save that and maybe add a new one, mobile, mobile, right? So this subcategory, they don't need colors. They just need a parent.
So now when I refresh here, there we go. You can see how this looks like mobile back end front end, right? And if I go ahead and go inside of my categories and create a completely new parent category, for example, let's call this design. Let's call it design. Maybe design and UX, something like that.
And let's add some fancy color here like this. And there will be no parent, so you can just save it like this. So now, there we go. You can see that design and UX is a new component here. But since it has no subcategories, the menu never opens, right?
We only open the menu if we see that it has subcategories, right? So let's go ahead and just confirm that it's working by adding a new subcategory here. I don't know, let's call this user experience, user-experience, like this, and save this. And let's add a new one called user-interface, user-interface and click save. So basically just adding something And there we go.
:01 So this is what we were trying to achieve. And it looks pretty good. So what we want to do next is we want to implement the proper way for them to render responsibly. Because right now, well, I don't think you can actually see it because it's only two of them, right? But you can see this is not exactly usable on mobile, right?
:28 You can't exactly hover properly here. So we will handle proper mobile mode so that it can be accessed by mobile as well. Great. So I think that marks the end of this chapter here. This was what we wanted to achieve and we did it.
:48 So let's mark this with this. And now we have to push to GitHub, of course. So this is called 05 Search Filters. So we do have some errors here. So our code rabbit will definitely tell us some advice, but I'm going to leave it like this for now.
:04 So 05 search filters. So let me shut this down. Git checkout v05 search filters. Git add. Git commit 05 search filters.
:18 Git push u origin 05 search filters. There we go. As you can see, I am now in my branch 05 search filters and I have separated from the master or main. And now we're gonna go ahead inside of GitHub. And there we go.
:36 I'm going to open a new pull request, make sure the base is main or master and compare is 05 search filters. There we go. I'm going to click create pull request and we're gonna see what our reviewer says about our code. And here we go. Summary by CodeRabbit.
:59 So This pull request introduces a new database migration script in package.json and update is the asynchronous data retrieval in the layout component to fetch and format category data. That's exactly what we did. So changes file by file, we added a new script to reset our database. We converted the layout to an asynchronous function so we can get our categories. We have changed home from an asynchronous data fetching component to a normal static display.
:29 It then describes how we created the search filters component including the categories, category drop down and everything else. And in here you can see the sequence diagram for fetching the categories in the layout. But here's an interesting thing. You can also see the sequence diagram of how our category drop-down and use drop-down position works. So when user mouse enters on category drop-down, it will calculate the drop-down position using the use drop-down hook and it will return set position and then it will render the sub category menu.
:02 And on exit, it simply closes the dropdown. And inside of here, it left some comments, which I already guessed that it's going to talk about, which is my any types. Absolutely agree. And we are not going to be using the type any. It is just temporary until we add trpc.
:22 And it also added, this is quite interesting, so it understands the correct structure, But it did not catch that we formatted this into our without docs, right? We removed the pagination element from it. But still very interesting that it is aware of how it looks directly from the API. So no need to change any of this, of course. Same thing here, right?
:51 It's just mentioning these things. It noticed an invalid href, which is something we're going to add later. But this is an interesting one, which I've noticed. Oh, yeah, this is cool. So improve touch device support.
:08 The dropdown currently only appears on hover, which doesn't work well on touch devices. So that's interesting. We could refactor this. So on click toggle drop down, right, instead of just on mouse center. That's very interesting.
:24 We could do that as well. Yeah, it wouldn't harm us if we were to add on click to open the drop down as well. And here's another interesting one. So it enhanced accessibility for dropdown menus. So if we use the enter key, right, or escape key to close it, and it added the area attributes here.
:47 So very useful for us, right? So at the moment, I'm going to stay true to my source code. But these are some very, very useful suggestions. Very interesting. It also tells me that I left a console log and some other recommendations here.
:04 Very, very cool. Great, let's merge this pull request. I am satisfied with how it is at the moment because a lot of it is just temporary. I'm not going to delete the branch. So now instead of here, I should have 05 search filters.
:19 And now what we ought to do is checkout back to master or main. Here's a little tip. You can use git checkout and then just an empty dash, and that will bring you back to whatever branch you were previously on, in case you didn't know that. Otherwise, just use this or, of course, main if that's what you use. And now let's do git pull origin master or git pull origin main and git status to double check.
:51 And there we go. Everything is up to date and let's just check the graph. There we go. So we detached into a new branch, 0.5 search filters. There we go.
:02 And then we merge that back inside of master. Excellent. Great, great job. Let's just go ahead and mark this as complete as well. Amazing, amazing job.