Build a Full-Stack Filtering System with Next.js 14, Tailwind, Upstash (2024)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
yo what's popping man in this video you and me will build a complete e-commerce filtering solution from scratch together once a user visits your website you're going to see beautiful loading States for all the products and here's the main thing on the left side the product filtering functionality the main thing we're going to build in this app because these product filters are some of the ones that the e-commerce product giant Etsy uses and we are going to implement them together you can filter products by color by size and here comes the best part even by price in certain ranges including a completely custom range that you can set via a slider now in this entire project I want to teach you two extremely important tools you're going to see in open- Source projects you're going to see these tools in Enterprise projects they're incredibly popular and the reason is they're damn useful and for once that's react query in its most basic form react query allows you to send data from the front end to the back end and back right that's what it does but things like loading States Arrow States caching and so on is all done for you with re act query super important tool that's number one you're going to learn number two is Zod a schema validation library and this sounds kind of woo woo like what the hell is a schema validation Library so here's the idea you define the shape of what a product looks like you can think of this for example you say that hey each product we offer in or store will have a size will have a price and so on after defining this shape once and that's the beauty of the tool you're going to learn in this video you not only get full Security on the back end that all requests made to your backend contain exactly that data that you're expecting so this is awesome for security but also you get full type safety in the front and back end so yeah this entire app is going to be written in typescript and we're going to get full type safety for anything that we work in along the way in this app I want to show you some very common useful web development conventions such as how do you define constants I learned this at my very first Dev job and it's been super useful ever since I want to teach it to you because chances are if you ever work in something like an Enterprise project you're going to see stuff like this you're going to learn how to debounce inputs what does that mean basically it's amazing for performance check this out as we move the slider each time the thumb is moved a request actually fires to our API that's not good for performance right with debouncing what you're going to learn in this video only after a certain time that you define has passed will one request actually be fired to or back end which is great for performance and just not kind of cooking your API right and all of this you're going to learn in a super excess ible and well-designed app and by the way as a bonus all the products that you see in the demo I have prepared for you all the assets are made available to you and we're going to use a seeding script together what that means is we're going to put all the products you can see in the demo right here into your own database so you can visually have something to work with the exact same data that I'm using in this demo right now so all you need to do is run the script and it's in your database and you can also work with it and show it off on your portfolio for example if you want to so it actually looks good now following along is 100% free no credit card no money for any tools in this video completely free from start to finish we're going to do everything I just showed you in this single video in under 3 hours we're going to learn all of this and also we're going to go through all Concepts in detail so why are we doing what we're doing so you understand the idea behind the code and can use it for all future projects and not just this one so you're going to be the Super Future proof developer that has the concepts in their head and actually understands what they're doing so enough talk I hope you are as for this project as I am this video was a ton of work and I'm super excited to share it with you right now so whip up your code editor get started boot up your PC whatever you need to do and let's get started in building this thing from scratch right now together so how are we going to go about this so the first step will be the UI design this kind of builds the entire foundation for everything that's to come later and then we will actually go ahead and add the functionality so that's going to be the entire filters for the for the size for the price and so on and but of course we need something to build on top of that first so first step will be making things look good and second step will be making things working and this is going to be the most interesting step because this involves like the entire thing that I just teased in the intro like react query like Zod for example schema validation um some common patterns you will see in web development and so on and so on that's going to be in this um functionality part but of course this is also super important um the UI design making things look good right so so let's get started in doing this and we will start in our Command Prompt and this depends on where you want the project to live right I'm going to put this on my desktop but you can put this wherever you want and then we will type in pnpm DLX or anything else like you could also use npx for this this is the note package manager you could also use yarn to create this project this really doesn't matter but I want my dependencies to be installed with pnpm out of the box which is a package manager similar to npx and so we're going to use pnpm DLX create next app at latest and then give it any name that we want for example product filters I'm going to call this and then hit enter now if you were to use npx then the command would look like this and I'm going to use pnpm DLX to do this and hit enter but it really doesn't matter what you use cuz the questions it will ask you in the next step are always going to be the same would you like to use typescript oh hell yes we do would you like like to use es L yes Tailwind yes Source directory I prefer this but this is personal preference um as I said I like it though so we're going to choose yes as well yes for the app router and no we don't want to customize the default import Alias now that's going to install all the dependencies let's give this a bit less space and with pnpm this is going to be really fast with npx this is going to take a bit longer cuz pnpm has some really nice builtin caching um implemented in it but once that's done implementing or downloading and installing all the dependencies and there we go it's finally done so we can get started with our first step the UI design so little Pro tip by the way if you want to open a folder in vs code we can simply CD into the product filters we have just created hit enter and then type in code dot that's going to open the current working directory we're in um up in vs code it's going to do it here on my second Monitor and there we are in a new nextjs project that we created with PNP yeah that's why we have a pnpm lock if you did it with npx then you would have a package lock or with yarn you would have a yarn lock right here so perfect very very nice so let's see if everything is working before we start anything let's see if the default setup is working and I just noticed I already have another app running so I just went ahead and stopped that real quick let's start this up again and that will start our project up under the 3,000 Port under Local Host so let's navigate there and we should see the basic blank nextjs page and if we do then that means that yes we have set up the project correctly and we can actually get started perfect there we are which means we've done the setup correctly everything is working as expected very very nice and we're going to start this entire thing right here in the page. TSX that was generated for us by nextjs so what we're going to do is remove everything that's already in here so we're going to Mark the entire main section right here and I have a little hotkey for this by the way I can just use contrl M and if you're wondering how to do that yourself you can go to file then preferences under keyboard shortcuts and this is called emit out yeah this one emit balance out word and I set this contrl m so if you want to do the same thing just set this key binding and that allows you quick Pro tip on the side by the way um to go into any HTML or XML element hit that shortcut and then the entire element will already be selected for you which is super handy and it will just allow you to code a bit faster but that's only side note right so this right here is where we're going to start so and we're going to start with a main section and this main section we can give it a class name because we already installed this with tailwind and this is going to be an MX of Auto a maximum width of 7even XL a padding X of four a on small devices a padding X of six and on large devices a padding x of8 and by the way if you've got the Tailwind extension installed and that will be this one right here Tailwind CSS intelligence that will allow you to hover over these class names and see which CSS is actually applied under the hood so what does Max with 7xl mean well basically it's just 1,280 pixels that's that's all it mean so it's a really handy extension um we're going to make use of here and by the way just to make our life easier let's also open the project up side by side so we can do uh so we can see in real time what we're even doing here so I'm going to say hello world right here and let's let's see if that appears and it does perfect so we're all set up to go and now of course you don't need to kind of follow along with the hello world here that was just an example and we're actually going to get started with a div element in here so this div is going to get a class name of flex an items Das Baseline a justify Das between we're going to give it a border dasb for bottom so only on the bottom will it have a border a border gray of 200 and then a padding bottom of six and a padding top of 24 now inside of this St we're going to put an H1 element a heading one large one and this is going to get a class name of text 4 XL a font of bold a tracking Das tight and if you're wondering what that means it's basically a letter spacing so the letters in the headline will be moved a bit closer together it looks really nice in Headlines by the way and a text Gray of 900 now what is this H1 going to say this is going to say high quality cotton selection and let's hit save now you might notice that hey Josh this actually looks pretty weird like the background is black the text is like this dark kind of grayish color how do we fix that this doesn't look good and you know what you're damn right this actually doesn't look good so what we're going to do is get set up with a UI library that gives us some really nice default CSS variables out of the box that are going to make our page look very nice the way we do that is by saying npx shat cn- UI at latest in it and that's the initialization command for a certain UI Library called ui. shat cn.com I use this on nearly all my projects right now and one of the main reasons is not only is it a component library that gives us access to a bunch of really nice looking accessible components out of the box but we also get the entire um color schemes with it and these look great out of the box with almost no configuration which is I really like this UI Library so to get started we're going to select the default style we're going to go with zinc as the base color but this really is personal preference this is like the Slate is kind of like a bluish gray I guess the zinc is just basic gray and there's some other tones you can choose from here um so I prefer the zinc would you like to use CSS variables for colors yes we definitely want that because that's actually going to make changes to the global. CSS right here that nextjs g gives us out of the box and sets these variables right here for both the light mode and also for the dark mode so whichever you want to implement you can do both um if you want to but the defaults it gives us will look very very nice out of the box so once we start back up or development server using yarn Dev or npm randev or pnpm randev and refresh our page we should be able to see that things changed visually and there we go I mean dude how easy was that and how much nicer does this look look compared to before like the difference the difference is just crazy let's be real here okay and all that just by running one CLI command for the UI Library hell yeah all right okay so perfect we can now continue under the age one knowing that our app will actually um look good now okay because again I don't want to teach you just like the functionality but of course you also want to have something to show for it you know that you can show to people um okay so the div we are going to create under the H1 element is going to get a class name of flex and items Das center now inside of this div these are going to be and don't you don't need to follow along with this I'm just going to type this here to show you these are going to be the Sorting options that are going to be on the right side right here and later above or product View and to make this happen if you if you paid attention to the demo this was like a kind of drop- down menu and we're not going to implement that from scratch either there is a super neat thing we can do in our command line and let's clear this for a second that is npx shad cn- UI at latest or UI Library add and then it's drop down Das menu and hit enter by the way all components you can already preview while this downloads right here so this is going to be the drop-down menu on ui. chan.com you could use any other UI Library if you wanted to I just like this one it's just personal preference and basically this is what the drop down menu is going to look like and that's fully access ible so what that means accessible is when we for example press Escape then the menu will close or when we click away and that we can also use arrow keys to kind of Select stuff right arrow key left Arrow key right here how cool is that right and that's all done for us out of the box um but we have full control over the styling right so let's start back up our development server once we install this drop down menu and what that does is basically it creates the component in our own project so there's no abstraction we can see exactly the entire component it basically installed for us right here under the UI folder in our components folder right so it basically created this drop-down menu. TSX file what that allows us to do is beautiful because we can now use this um it's called dropdown menu from our own source code right there's no abstraction so we're going to create the drop down menu right here and then to create an item in the drop down menu we need basically two things there's going to be the trigger of the drop down menu which is the button that opens it and then there's the content of the drop down menu which is super intuitive and that's why I really like this API design check this out we can create the drop- down menu trigger also from our own components and not from radx UI which it uses under the hood but we want the styled version of it so this needs to come from or or drop down menu in or code not from radic now inside of here we can say for example sort and give this trigger a class name because this is nothing else than like a button or div element we can actually see what is it um so this is a react HTML button element right so under the hood this is just a button that is wrapped for us in this kind of little abstract way um to give us a nicer API to interact with and this is going to get a class name of group an inline-flex a justif oops justify Das Center a text of SM for small a font D medium a text Gray of 700 and on Hover we're going to give it a text Gray of 900 just to have a little of um mouse cursor interaction once we hover over it so let's move this into a side by side once again and if our Dev server is running we should be able to see those changes show up right here where it says options so let's let this reload and we should be able to see the trigger which is nothing else than a button element now when we click it nothing happens yet but it does have the correct kind of cursor and everything so we know we set this up correctly now right below the sort we're going to put a little icon and this icon is going to come from an icon library that is called Lucid react now low key I use this in every project that I do this is a super nice completely free kind of Icon library that gives us access to all the svgs and let me show you what this does let's give this a bit more space we can go over to icons and you can see all the icons that are in this Library it's super cool there's a bunch of them like 1.5k almost which is pretty nuts if you ask me and we can simply install this and use all the icons as is it's it's honestly super cool so the way we're going to install this is by saying pnpm install Lucid D react and hit enter that's going to install the icon Library package into our own app so we can simply start back up or development server and then use the icons right here in our app now the icon we want to use right below the sortex right here is the Chevron down icon from lucd react and this is going to get a class name of minus Mr minus one so we give it a negative right margin that's all this does a margin left of one a height of five a width of five a flex shrink of oops shrink of zero there we go a text Gray 400 and we're also going to give it a group Dash hover and I'm going to get to what this does in a second here of text gray 500 so a bit darker now what is this group hover all this means is because we declare the group up here when you hover over the trigger then also the same effect will apply to the Chevron down now let me just show you what this does so you're going to see the icon show up right here next to our sort text once this loads and sometimes nextjs takes really long to load but also my PC is pretty old it's like seven seven and a half years old so sometimes it takes a bit but anyways um once we see the icon right here you can see when we hover the sort text even though we're not hovering the icon itself it will still get a bit darker and that's what this kind of group does right here and the group hover it's it's pretty useful so right below anyways uh right below the drop down menu trigger this is where we're going to continue with the drop- down menu content and you see how cool the API design is like how intuitive we have the menu we have the trigger which is the button and we have the con content like I really like this API design now the Align property align of the drop down menu content we're going to set to end and you're going to see what that does in a second it determines the position of and why is this not a side by side anymore there we go it determines the position of where the drop down menu will open and inside of this drop down menu content we want to display all the Sorting options there are and now comes one of the parts I mentioned in the intro so how do we Define the Sorting options like in a really clean kind of understandable readable Manner and what I'm about to show you is a convention that is super common in like uh in basically everywhere personal projects Enterprise I've seen it everywhere but especially important in Enterprise it's that at the top of the file we can declare constants and we're going to put them in all uppercase now the uppercase indicates that this is a constant value it's a magic thing that we Define somewhere that is never changing and it is a really useful convention to put that in all uppercase so when you see it later on in the code you always know this is a constant and this never changes again this will get very clear as we kind of go through this together and you'll understand exactly why we do this um convention so each sorting options that we Define right here will get two things it will get a name and this is what's going to show up actually in the drop- down menu so for example none and it will also get a value and the value for the none will also be none right here now the next array element is going to get of course the same properties a name and a value the name is going to be price and then low to high and the value of the Sorting option will be price Das Dash ASC so as sending we go from low price to high price and this is later how we will determine the actual data fetching logic from our database so the value is useful for us in code and this is what's shown to the user now last thing and by the way Pro tip if you go into a line in vs code it holds shift alt and arrow down you can simply copy the line down now we only need it once but you get the idea and we're going to change this the other way around is going to go from high to low and then the value will be of course you can probably guess not ascending but descending from high to low prices that are already all the Sorting options we need and we're going to add one more thing to this and that is the as const right here now you might be wondering what does the as const do and by the way this is one of the most powerful features of typescript so imagine this we don't have the ascon right now and we hover over the sort options and we see it's a name and value string array if we add the as cons now the thing that appears when we hover over this fundamentally changes because typescript now knows this is always an array that is exactly three elements long these right here and it has exactly the string values that we can see right here these never ever change we cannot push anymore so if we try to push into the sort options which is an array so we have the push option we can see this won't work because property push does not exist on type readon array right so basically if we didn't have the escon yes we could push into it with the escon we're telling typescript um that this will never ever change in length or value and this is really useful for type inference as you're going to see in a second here so now for the drop down menu content let's finally finish that when we click on the sort we want a drop down menu to open with the content of the sort options how do we do that well we can simply map over all the sort options and for each option that we get from that we can render out some jsx now the jsx that we're going to render out is going to be a button and because we're mapping over something this first jsx element always needs a key and this is going to be the option. name so this is a unique key for every button that we're mapping over basic react right here you probably already know that and this is going to get an onclick Handler now what do we want to happen when the user clicks a sorting option understanding this is the most fundamental Concept in the entire app that we're building right here so we're going to have a bunch of filters like the sort for example the um size we're going to have the color and so on and imagine this down here is the actual UI that the user can see and interact with UI and basically when the user changes any filter that is visible to them in the UI then we want to keep track of those changes in state that we can later then send to the server and this looks kind of bad there we go so when the Sorting changes for example in the UI we want to make sure that we keep track of all those changes and of course similarly for the size and also same thing for the color if anything changes down here we want to make sure we keep track of that so we can then later send it to our data base the question now is well how do we keep track of that in state in a really readable nice kind of way and that is a fantastic question so what data do we have to work with here well basically we have the option. value which is the actual sorting kind of I guess algorithm you could call it and that we want to use for our products right this is what matters to us in this onclick Handler the option do value now we need to store this value in state to keep track of it to send it to the database how do we do that well by declaring state right up here in our component so let's call this the filter and the set filter of course by convention and this is going to be equal to used state in its most simple form right and because we're using nextjs 14 this is by default a server component it's rendered on the server but if we want any kind of interactivity like this react hook then we need to mark this component as a use client component right here in the very very top of our file even above the Imports that's how we are able to use the react hooks right here now how do we make the state super readable super easy to understand when anyone looks in our code when we need to maintain our code this is the approach that I came up with and I really like this one so in its most simple form we want to keep track of a property and that is called the sort now instead we always need to give default values for anything so the by default the sort is going to be none this right right here and then we can change it to price s sending price D sending or we can change it back to none if we want through the drop down menu now what this allows us to do is when a UI change is made right here in the Sorting then we can now put that in this filter by using the set filter function now enough Theory how do we do this in practice how do we now use this well basically in the onclick Handler we can simply invoke a function and that is syntactically not correct there we go and whenever you click the button for the corresponding sort option we want to save that in state so we can set the filter state to whatever it was previously which we receive as the first argument and then directly return an object and this object will contain everything that was previously in the state which later will be all the other filter options like the size and the color right now we don't have any but this will be important later and then of course also the sort and the sort now is going to be the option. value so semantically all this means right is keep all other options that were already set like the size and the color and only change the Sorting to whatever the user just selected which is the option. value all right and then inside of the button we can simply State the option. name now I know this was somewhat abstract but we can simply log out the filter right here console log filter and I will show you exactly what this does so let's open up the console and let's refresh the page now we should be able to see that when we click this okay this doesn't look this doesn't look good yet we will get there trust me but when we click for example price low to high we can see this is now successfully saved in or state is price ascending price descending and when I now click none then we will see the sort is set to none and all the other filter state is kept as it is because we spread it in right here now of course this doesn't look super good yet so let's fix that that let's make this drop down menu actually look kind of not like Dark Water right so we will pass this a dynamic class name using our CN helper function what is the CN helper function if you're not familiar with it this is a utility class name merging function that our UI Library actually installed for us during the initialization step what this allows us to do is to pass a default class name right here inside of this function so this is going to be a text left a width of full a block a padding X of four a padding y of Two and a text of small and this class name will be applied unconditionally so always this will be applied and then we can combine this with optional class names that will be applied conditionally for example we want to apply a text Gray 900 but only under the condition that the option. value so what is rendered out here is equal to the filter do sort so what is currently selected as the Sorting filter and by the way we can also apply a BG gray of 100 in that case and then the second conditional class name we're going to apply is going to be a text Gray of 500 when the exact opposite is true so when the option. value is not equal to the filter do sort so for any other option once we save that you can see exactly what this does first off this now looks a lot better and secondly the active thing is currently now selected kind of highlighted in the text and it just looks a lot nicer because it's very clear to the user which option is currently selected and which ones aren't beautiful that is our sorting and we're already saving this in state which is a perfect prerequisite to later send it to or API to actually get the product results now let's add a button so right below the drop- down menu and this button is going to get a class name of minus M minus 2 a margin left of four a padding of two a text Gray of 400 on Hover this is going to get a text Gray of 500 a on small devices we're going to give it an mL of six a margin left of six and on large devices this is going to be hidden so this is going to be the mobile filter button and inside of here we're going to use the filter icon from lucd react that's going to look really nice and this is going to get a class name of height five and a width of five and that's it so basically what this does is it only appears on very small devices and it's going to be the little filter icon that when we click it you can Implement like a mobile drop- down menu whatever you want if you wanted to show the same filters that we're building in this video on mobile as well now one thing I want to do right now with you is implement the data fetching so we can Implement all the filters that we want but if we don't have products to see this entire thing and demonstrate it literally visually then I don't think this makes too much sense so what's way more fun is first adding the products in and then actually working on the filters so we can see what we're doing in real time and to do that we are going to create one new folder and this is going to be our database folder so this is where all the database related stuff is going to live this is going to be in our source um directory let's create a new folder and let's call this DB for database and let's create a new file in here and this going to be the index.ts from which we are going to um create our database so we have access to the database throughout our application now your choice of database of course really depends on what is the app that we're trying to build in our example we're going to use the up stash Vector database full disclosure of course I work at up stash right it just makes a lot of sense for me to use this it takes like 1 minute to get set up so we can actually Focus the important stuff which is the filtering however of course if you want to use postgress instead or MySQL and so on and be my guest no problem at all you can totally follow along with these as well um I think just up stash Vector is the easiest way to follow along with this and of course it's also totally free now once we navigate it over to up stash for this let's go to the vector Tab and let's click create new index and uh let's call this index what do you want to call it let's call it product filter as well the region is going to be Ireland for me so in the EU and the dimensions of the vector are going to be three later on this is going to be the product color the product size and the product price um so three dimensions make sense here and as the distance metric we're going to use the ukian right so very very brief explanation ukian versus what is the alternative the coine right here what's the difference basically idian distance measures the actual distance between two vectors right so the ukian distance measures this distance right here versus the cosine distance measures the distance or the difference in the angle between two vectors so it measures the alpha right here that's cosine this is ukian and ukian is especially useful in smaller dimensional Vector spaces which um we have with three vectors right so we're going to use ukian distance and three dimensions and hit next pay as you go is fine for me but of course you can use the free plan but they already have my credit card data so I'm going to use um pay as you go and then all we need to connect to our database is uh under the details right here it's going to give us the uh the EnV stuff that we need to connect to our database so we can simply copy this over go into our project into a EnV file where secure environment variables live paste this in install one dependency which is the pnpm install at up oops at up/ Vector let's give this a bit more space so you can see easier what I'm doing here pnpm install at up Vector hit enter and that's literally all we need to do to connect to our database so we can simply go into a index file for the database that we created and then just already established the connection to our database which is as easy as export in a con DB and this is going to be equal to a new index we now get from the up stash Vector package we can simply invoke this and this is literally all we need to connect to our database that's why I said following along with up is by far the easiest one again for disclosure of course I work there you can follow along with anything else if you want and but this is just incredibly easy to actually focus on the product filtering because that's literally all we need and it's automatically going to pull the values from or. EnV file and use them to to connect to or database now into this index we can actually pass a typescript generic and this is going to be really useful because let's take a look at what this looks like we can define a type product now what is this for passing the generic into our database connection right here so imagine each product has an ID which is going to be a string it has an image ID which is going to be a string as well and by the way I recommend you to follow along this is going to be important for data this product or each product rather has a name that's going to be a string as well each product will have a size and the sizes are going to be either an s or an M or an L each product will also have a color and the colors are going to be either white or they can be beige or they can be blue or a product can be green and lastly a product can also be um purple oops purple and these are some colors that I just prepared for this video of course you can have more colors less colors um if you wanted to and each product will of course also have a price and this is going to be a number now what's the benefit of passing the generic into our database connection right here basically imagine this we query data from our database by the way you don't need to follow along right now I'm just showing this to you we await a databasequery in query some vectors right so for example we could use some information here to query we're going to get to that later it's not important right now um what is important is that all the vectors that we get back in the response so for each Vector each Vector will also contain the metadata that is associated with that vector and if you notice if we hover over this the metadata is now actually typed it's type save so if we were to access a property on here we can see it has the color ID image ID and so on and so on so all the properties that a product has that we passed as a generic here if we didn't pass this generic there would be no type safety that's why we pass it right here it's super super useful for later um so we can um work in a completely types safe endtoend kind of way now how do we get demo products into our database and the answer is actually pretty simple by doing a seeding script seed dots that's what I mentioned in the beginning which which I already completely prepared for you now at the time I'm recording this video there is no GitHub um link I can give you for this project of course there will be when you're watching this video right now it will be first thing in the description however while I'm doing this I just have this open in a secondary project and we can aate into the public folder copy all images that are here and then copy them over into our local public folder by the way we won't need the svgs right here let's get rid of them so go into the GitHub link that's in the description for this project and simply copy over the images these are all the images we need for all the demo products that are going to be in our database I prepared every single one of here in Photoshop in the different colors and so on just so you have a really nice um looking project so we can see very visually what our filters do here on the right hand side and also I prepared the entire seeding script so you can find that under Source DB and then C.S in the GitHub repository and you can simply grab the entire seeding script this is not important to understand right so I want to teach you how to write good quality software in this video the see script is not very important for that so it's totally fine to just um go ahead and copy and paste that into the seat. TS we have just created now we're going to get one or two errors here in our seeding script and that's totally fine we're going to fix them right now together so import type product it doesn't know where the product type is coming from that's um understandable right because we're not exporting it so we need to export the type from our database and it also doesn't know where these um colors and sizes come from so that's because I did it a bit different in the original project but I don't really like that approach anymore so I think a better approach to solve this problem is to declare a const and this will be in uppercase colors right here and this is going to be equal to all the colors we have just defined in our database file so we can simply grab all the colors right here copy and paste them right here of course that's going to give us a syntax error because this is um typescript syntax and not JavaScript so we need to wrap this in an array and replace all the pipe operators with a comma and we can also Mark this as as if we wanted to um just to tell typescript that hey again this array will never change in length or in content and the same thing we can do for the sizes so the sizes um right here are going to be also in Array and same thing right here can grab the sizes from our index.ts paste them right here and replace the pipe operators with a comma and if you wanted to again and this is just personal preference also Mark this as as const because this will never change last thing that's giving us an error right here is that we don't have the EnV package super simple fix pnpm install EnV minus D for a development dependency we don't need it in production this is just for our local development so this seeding file which is separate from the rest of our application can actually read from ORV so it can connect to our database and push all the products um inside of our database so what is it actually pushing into our database each product with an ID the actual Vector which contains um the product color the product size and the product price and also as the metadata we are attaching all the properties that a product has like the ID the image ID color name size and the price now the details here are not too important to understand if you want you can kind of um pause the video right here and get it more detailed overview but the important thing is that it just pushes products into our database that we can work with in what's important in this video which is the filtering functionality right and all we need to do for that to happen to get the products in our database is to define a seeding script in our package.json now this seeding script is going to be yarn TSX or you could use any other manager as well I just normally use yarn for this/ Source DB seed. TS so basically we're using a TSX package to execute the file we have just defined the seeding script and this TSX package is a super simple package that we can install pnpm install TSX minus D once again is a development dependency it allows us to super easily run typescript files that are separate from our app and normally with like es build and everything um this would be a bit harder but TSX makes that super easy so all we can do now to push the products into our database is to run the seeding script right here we have just defined and if we want we can also go into the data browser in our database and take a look at that in real time so once we execute the sting script oh we get an errow of course we do right let's see what the problem is up Vector rest token is missing okay and that's probably did I forget yes so what we forgot to do is also um to use EnV right here in our database file so right now because this seing script is separate from our application and it Imports our database the database will also be run in a context that is separate from or nexs application in nexs you can by default read from the EnV in a separate file you cannot the fix is extremely easy we can simply import everything from uh or ASV and then from dot EnV just like this and simply call the env. config right here and this allows this file even if it's separately called from our nextjs application to read from our EnV file let's clear the terminal CLS and run the seaing script again and hopefully this time right there done in 1 second we can see in our database if we go ahead and refresh this that the vectors are here so there's a lot of products um 20 to be specific that have been added to our database and they allow us now to really visually um apply all the filters in our application we can see all the product datails here like the ID the image ID this is absolutely perfect the name the size very very nice and what that allows us to do now is to actually fetch the data from our API in our own application and display the products right here on the page before we get into the other filters it's just going to be nicer for us to work in so how do we get the products to show up on our page and the answer is well by fetching them from our API route now that's easier said than done right not really because there is one um really cool package that is called react query what I also mentioned in the very beginning of the video react query is a super common and super good both of them um utility for data fetching right of course you know you could use like the regular fetch API to fetch data from SL API products and or however you design your API yes you could do that but what about loading States what about error States what about caching synchronizing server State and so on well that's exactly what 10stack query solves it's very very common you're going to see it in a lot of places the company I work at we also use it to fetch data in our console um especially in Enterprise and personal projects everywhere basically it's a very very common thing and the package is called at Tans oops tanack react D query if we type that in you can see that right here published by Tanner linday 1 hour ago well that's pretty recent and how many yeah it's got over 3 million weekly downloads as I said it's U it's super common people like it a lot and for very very good reason also the setup is super intuitive so we're going to say pnpm install at T stack SL react um- query and hit enter now how do we set this up how do we fetch data from our server I don't think this is going to take longer than 5 minutes and you're going to see why first thing we want to do is create a new um file in our components folder and call it the providers. TSX now first thing we want to in this provider's file that we just created is marking it as a client component by saying use client and then declare this as a normal react component right so the con providers is going to be equal to an arrow function that we export default at the bottom providers there we go now inside of these providers we are simply going to return the query client provider and this should be Auto imported from T de react query that we just installed it doesn't so if that ever happens to you simply hit um shift control and P and hit developer reload window that's going to restart VSS code while keeping all the like terminals running and so on if you had that running and then most likely the Auto Imports wow wiah they suddenly um they suddenly work so the providers all they do is they contain some react children and they provide context to the rest of our application to receive leave the children we can use them as a property as a prop right here receiving them in the component and this is going to be of type props with children a utility type we get from react and because the children are the only thing that we want in this component we can see simply pass it an empty object generic right here and that's just going to type the children for us of course we could also do that ourself but this is just a really nice um Pro tip and then inside of the query client provider we can put our children which now means that all the children further down in the Dom in or react application will be provided with react query because this is what we need to do to make react query work and this also takes a client right the uh the query client provider also takes a client and this client we will create above the component to make it cached and this is going to be the cons client is going to be equal to a new query client right um if you don't really understand what's going on this is not a problem this is something you do once in the application and then it basically just makes you able to use react query right it's not too important basically the query client is a cache that we can use to keep track of the data that we get back from our um API rout it's very useful and the last thing we need to do is go into our layout. DSX and now wrap our entire app with these providers oops providers right here and then provide the children that we get in our layout which is by the way created Auto automatically the layout by nextjs for us simply wrap the children in these providers and that's literally all we need to do that's the react query setup we can now get started in using it which is the much more um kind of fun much more important part right so how do we use it in or page. TSX we can simply get started in fetching data from our API by using the use Query hook we get from tack react query this is for get requests to or API endpoint if you wanted to do um all alternative request you could use the use mutation if you wanted to change data the use Query is for reading data now inside of here we can pass an object and pass this a query key and the query key is an uh array of and we can pass a string in here the products so this is what the result of this data fetching will be cached as and if for example we had a kind of constant in here like or a filter if the filter changes then this query would automatically be invalidated from the cache so this is for the caching mechanism it's super super cool it can get much more advanced but I don't think that's really useful to get into right now this is the most simple way to go about things and this is perfect for what we are trying to do and then in the query function we can pass this the actual function that will Define how we fetch our data so in this function you normally always call your back end or any other service that you want to fetch data from in or case that's got going to be our local backend and we could do this using the normal fetch um kind of API I prefer just syntactically to use axios for this axios is an HTTP um Handler or HTTP client that we can use I think it it is like super super popular let's see yeah over 50 million downloads it's it's kind of nice um it's just some syntactical sugar you can totally do this with the regular fetch API but I prefer to do this with the axos package so pnpm install axal and I know like we are installing a lot of packages right and that's true but it is also very common to have um a lot of packages in your actual like production settings so it's It's Not Unusual to use a lot of packages for stuff like this so what exus allows us to do is to make the Syntax for fetching data really really um intuitive so we can say con and then worry about the destructuring later here is going to be equal to axos and we need to await this because this is going to be an asynchronous call to or back end do post and by the way we also need to import axios at the very top import axios from axos so we have access to the package right here where we need it and the cool thing about axos is that it makes typescript or type safety super easy by passing in a generic right here before we actually invoke the function now what can we pass in here basically the result of what or API will give back to us and what we will um send back from the API is going to be a query res result which comes from up Vector and this also takes a generic in which we can pass the type of the metadata of each product that we get back which is nothing else than our product um type that we defined in our database file right and we also are expecting an array of this now for the actual um function that we will invoke to fetch the data right first off what is the endpoint that we're calling and this is going to be HTTP and then SL localhost 3000 SL API SL products and of course if you deploy the to production then you could also use the production URL right here and then for the data that we will be transmitting to that endpoint these are going to be our filters so as the filter that we're going to send along for each request we make to get the data we can send along right now the only filter that we have which is the Sorting filter and this is nothing else than the filter do sort as easy as that that's why I like this API design so much it makes the request look super super readable and you're going to notice the same thing later once we add the color and price and so on it will remain as readable as it is right now so what we can destructure from axios is the data that we get back and we now already know the type of the data because of the generic that we passed into here this is why I think axios is so useful for this and we can simply return the data from or use Query now because we return Cent the data we can now use it as the data that we can destructure from the actual hook and we can also rename it to products for example if we now hover over this we can see if this is the actual type from our API or this will be undefined while it's fetching the data once the page loads which is totally cool now for now to know if we set up everything correctly let's simply log out the products and of course what we still need to do is Define the actual API route right there is no backend serving this route yet but that will be very very simple to create let's do that in our app folder let's create a new folder called API and inside of this API folder let's create a new folder and call this products now this products folder that we just created will contain a new file called route. TS and this is important to name it that because that's what nextjs expects of us to name the file to actually make this available as an API route and inside of this route we can simply export a const post so this is going to be the HTTP verb that we want to serve as a uppercase kind of word so this could also be get this could be delete we want the post method to be served from this API endpoint and this is going to be an asynchronous aror function that's going to handle all the logic for now right just for demonstrative purposes so we can already see the products in our application let's already query our database and then later kind of um optimize this whole thing right but for now we're going to say the cons product products is going to be equal to await DB or database do query so that's how we call or search for data in or vector store and we need to pass the some options like the top K how many results do we want to get back and I think 12 results for now is totally fine then we the vector that we're searching for in our case for now again this can just be 0 0 0 um we just want to show some products in our app for now we're going to tweak this later and we're also going to include the metadata um so we get access to that in our database result in the type right here perfect okay now we can already return this from our API endpoint so return new response and then json. stringify so we pass these as a Json string back to our front end the products and that should already work now there's no error handling or anything uh we're going to add that later so this is not what you would deploy to production at all right we're gonna uh worry about that in a second but for now let's just see if everything works if we actually get some products showing up in our console because we should right we're querying the back end from our front end using react query now and we also created the route that answers the request we're making from react query so ideally we should see the products logged out in our console and we can already oh we're not logging out the filter anymore that's fine okay and there are our products queried from the database using react query with the metadata the image and the name and so on perfect so this is the ideal foundation for us to render out um each product and actually show it in our UI we can see everything works we set up the front end and the back end correctly and wir them together using react query beautiful so the only thing that's left to do now to actually see the products is well show them and where do we show the products and the answer is we're going to do that right here before the main section ends and after the button and after the two closing divs so this is where we're going to start a section and this section is going to get a class name of padding bottom 24 and a padding top of six to give it some spacing from the other elements so it's going to look nice now inside of the section we're going to create a div and this is going to get a class name of grid a grid Das calls D1 and if you're wondering what that does it's the grid template columns repeat CSS property just in a much nicer Tailwind um kind of syntax and behind that we're going to give it a gap X of 8 a gap oops Gap y of 10 for some vertical spacing and on large devices we're going to give it a grid call of four now inside of this div later on the filters are going to live and these filters are going to live inside this div that we're creating right now here so we can even add a little comment right right here in our jsx that these are going to be the filters inside of this div right here however we're not going to do that right now because remember we want to show the products first so we can visually see what we're doing and for that we're going to create below the filters a product grid that's going to contain all the loading States all the products and all the you know product related logic and we're going to do that inside of a UL an unordered list and that's going to get a class name of large call span 3 then is going to get a grid a grid calls of one on small devices oops on small devices a grid calls of two and on medium devices a grid calls of three and lastly a gap of eight now inside of this UL the actual products are going to live so let's render out the products by mapping over them we already have access to the products remember that's what we call the data we get back from react query this is optional however and we're going to map over these products and then in return so um show some jsx right away now Josh why are these um optional why could these be undefined because once the page loads for like a split second half second second whatever how long it takes for the network request that is made um to our back end to come back right here you can see the products are fetched from the back end and this request um to the back end let's see how long it took uh right here in the timing it took 133.7k and let's define that component because of course it doesn't exist yet we're going to do that right here under our components um folder let's create a new folder a folder not a file and call this products now why are we doing this well because there's going to be later three different components related to a product which is the loading State the skeleton and the product itself we're going to start with the product itself which is going to be called product. TSX and the component is going to be pretty straightforward let's first create the eror function cons product is equal to an eror function and of course as always export that as the default at the very bottom now what are the props that each product takes and that's just going to be the product and we can even inline the type because that's literally the only prop we expect it's the product and this is going to be of type you can probably guess the product type from our database file as easy as that now let's get to the J X for the product so let's return turn a div at the top level in here and the class name of this div is going to be group and relative now inside of this div let's create one more div and this is going to get a class name of aspect dh-1 and aspect oops aspect dw-1 a withth of four in overflow D hidden so if anything kind of goes beyond the bounds of the div it's going to be hidden by default a rounded Das MD a back gray of 200 on large devices you want an aspect d none and we're going to say group- hover and this is going to get an opacity of 75 and on large devices we're going to give it a height of 80 great now inside of this diff we're going to create the actual image that's going to be the product image now what is the source for this image this is going to be the product Dot and then you can probably guess what it is the image ID right which is pointing to the path the image is stored at in the public folder and that's all the image IDE is this alt tag can be pretty much anything let's say product image and as for the class name that the image gets this is going to be a height of full a width of full an object Das cover and lastly an object D Center perfect let's format the file that is our image and if you've noticed we are very intentionally not using a next image right here that is automatically optimized but I really dislike the rendering strategy the image uses and I think the default HTML image looks much much better in the final product so right below this div right here and with one more closing div to go let's create another div and this will contain the product name price and so on so this div is going to get a class name of margin top four a flex and lastly a justify between to kind of separate the elements and so this is the Justified content property that we can apply because we're also using the flex now inside of this if there's going to be an H3 element but before that I forgot there's going to be um one more wrapper div um before that that's going to contain both the H3 that we're going to create in here and the H3 is going to get a class name of text SM for small and a text Gray of 700 and everything that's going to be in the H3 is going to be the product dotame as easy as that now below the H3 we're going to create a ptag a paragraph element with a class name of margin top one a text sm4 small and a text Gray of 500 and inside of this paragraph we're going to say size and then dynamically the product do size. to uppercase just like this um so this is going to be like s m or l or XL XS if you wanted to add more sizes and secondly with a comma separated dynamically the product do color so this going to be like black beige white and so so on perfect after the closing div with two more closing divs to go right here we're going to create a P tag and this is going to get a class name of text small a font Das medium and a text Gray of 900 and inside of this ptag we're going to say the product. price how much is the product there we go let's save this product component because we are already done that's it and we can now go ahead and import that in our M page. TSX now of course we're still going to get an error right here because this product expects a prop and why is the import not working I suspect yeah okay so we have the type product and then we also have the component product of course that can't be um the case so how about we fix this by aliasing the product type let's say as T product for example so that's the type product because this is actually a type under the so we can use the T whatever convention in typescript to rename it but we are using the type right here for example so we need to replace the um query result with the actual type weird because in the in the product that uh in the project that I made this didn't give me an error but apparently here it does anyways because we're rendering out the product component right here this expects a property of course which is the product we can simply pass in for each product that we're rendering out we get access to the product right here in the um function and we can pass in the product. um metadata and we can tell typescript yes we are sure this exists now why can we tell typescript yes we are sure this exists the reason is that we include the metadata wherever we fetch the data right here right so each product has metadata which is like the name price and so on and we're explicitly telling our database yes give us that metadata so we are sure this will exist at runtime perfect let's save that and let's actually see what happens is our server started yes it is and there we are there are our products beautiful from the seaing script into our database to our API Rod to our front end just like that and now we can very visually see um all of the changes that we're going to make the filters and um all the like the the Sorting filter for example and all the filters that we are about to buildt right now now while we are doing the products how about we quickly Implement also like the beautiful loading States for the product cuz if we refresh the page right now it looks kind of it doesn't look good let's be real here okay uh I mean it looks fine once the products are there but the loading could look a lot better so let's quickly do that because as you'll see this is really really straightforward so the way we're going to do this is let's go into our products folder and the components create a new file called Product skeleton. TSX and this is going to be cons product skeleton an arrow function function as always that we export default at the bottom the product skeleton now all this component will do is be basically like the product right it's going to mimic exactly the height and size and everything of a product but it's not going to contain actual information but just a loading skeleton that we can show while the stuff is loading for that we can return a div at the top level with a class name of relative and here comes the important part animate Das poles that's what's going to give us this um loading kind of aesthetic this is just a CSS animation under the hood and it's super super helpful and you're going to see that here in a second when when we actually use this component inside of here let's create a div with a class name of aspect H1 an aspect width one a width of full an overflow Dash hidden a rounded DMD a BG gray of 200 so the background color on large devices in aspect d none and lastly on large devices a height of 80 now as you'll notice this actually mimics the class name we wrote earlier for the actual M product that's how we know it will look the same because after all you can just kind of the way I usually do this is copy and paste the actual thing that you're trying to implement a skeleton for and then remove all the dynamic Parts like the size and so on and replace them with a just div that is uh gray background so it looks like the loading State now to achieve this we can put a div right here third one and this is actually going to be self closing we don't need anything inside of it and let's give it a class name of height full a width full and a background gray of 200 and this is going to be the image replacement on the product skeleton below um the closing div and after the self closing div as well with one closing div to go let's create one more div and this is going to get a class name of margin top four a flex a flex Dash call and a gap of two for some separation and then two self closing divs in here let's start with the first one this is going to get a class name of background gray of 200 a height of four and a width of four and then we can hold shift alt and arrow Down Bam there we go we just copy pasted that down and now let's implement this while the products are loading we want to show this loading State and it could not I'm not kidding it could not be easier with react query on how to do this so essentially what we can do is turn this current rendering part into a Turner check so if we have products if products then we can map over these products unconditionally because we know they exist now right because of the Guard Clause right here we can check if they're not undefined then we can render over them and show all the products but if they are undefined while they are we're actually going to map over a new array of a length of 12 where each element we're going to fill with a null Val value because we don't care about what's in the array we just want 12 elements to map over and for each array element we're going to map over this we don't care about the first value that we get back in the Callback function but we do care about the index of the current element that we're mapping over because that is going to represent the key for the product skeleton component that we are going to put in here so that's going to be the key and that's going to be the index and of course that needs to be products in the plural there we go oh and it seems like I messed up the the order it needs to be exactly the other way around because currently what this means is if we have products if the data is there then we're going to show the loading State and else we're going to show the product so of course it needs to be the exact other way around let's quickly replace the um thing right here the loading state with the actual product logic and then um cut this down and paste it right here so if we have products then we are mapping over the products and else that's what this means and we're going to show the loading State let's hit save and see if that works so once we are uh reloading the page right here you should be able to see yes it kind of works but did I mess up something with the um image oh yes and I did mess something up and that is I messed up these two first class names right here um we need to replace them the aspect H one and aspect with one so the intent here is that essentially we're making the product image a square right and in order to achieve that we can simply say aspect um- Square this is the better way of doing the exact same thing and if we save that and reload the page then we will see um yes this actually now works with the loading animation and if we go into like very full screen and reload the page it's a bit easier to see um the loading States right here very very nice they look awesome and you can see on the left hand side right here we already already have space for the filters that we are about to create very very nice so we can exit out of the product skeleton the product the route. TS providers layout whatever whatever we can close out of all of these and let's navigate back into our main page. TSX because this is where the magic is about to happen which is where we're going to implement the filters right DN now okay so the filters um are going to live right here where we already marked this inside of this div element now this if element is going to get a class name of hidden and on large devices it's going to get a class name of block so it's not going to be visible on mobile but mobile is going to be a bit different then inside of this div we're going to put an unordered list element it's going to get a class name of space y4 for some vertical separation a border border dasb for bottom a border gray 200 a padding bottom of six a text SM for small a font D medium and lastly a text Gray of 900 now inside of this unordered list we want to show some categories that you can browse through on our website essentially this could be like for example hoodies and shirts and pants and everything that you're offering on the website in the case of an e-commerce application and in order to make that happen we can Define these subcategories at the very top of our file let's put them right below the sort options for example let's say const and then again in uppercase subcategories because this will never change that's how we indicate this is a constant value and this is going to be nothing else than an array of elements each element as an object right here gets a name the first one is going to be t-shirts just like that it's going to get a selected property and the t-shirts is going to be true and we can also pass it just as an example in hre value that if you click on this then it will take you to that product page um of course this is just for demonstrational purposes and using shift alt and arrow down we can copy this down once twice and Thrice I don't know what that's called and then replace the t-shirts in the second one with hoodies and change the selected to false the third one is going to be sweatshirts and selected is also going to be false and the last value is going to be the accessor accessories it's very hard word do you spell it with two C's I'm not even sure it's a very weird English word uh and the selected value is going to be false great so now we can actually show those subcategories in our code and that's going to happen in the unordered list that we create so in this list we can simply map over these subcategories sub oops categories. map and for each category that's what we're going to name um each object in this array we are going to render out some jsx right away so in the normal parenthesis just like this and we need to prop a dude the syntax is completely messed up here there we go and we're going to close out of that um like that okay now inside of here we're going to render out an L element because if we're in an unordered list we expect the first or the top level element to be an lii A List element and it's going to get a key of the category do name so that's going to be like hoodies t-shirt and so on just a unique value to identify this Li element and inside of here we're going to render out a button this button is going to get a class name of disabled then a cursor not allowed there we go so we can separate this with a colon for conditional Appliance so what that means if the button has now a disabled state passed to it then this cursor not allowed class name will be applied that's just the Tailwind syntax um to do just that it's a really nice syntax I like it a lot then afterwards we're going to pass it a class name of disabled same thing colon opacity 60 so we indicate to people that this is not clickable if the button has a disabled State inside of this button we're going to render out the category do name so like t-shirts hoodies and so on and also this button is going to get a conditional disabled State and this is going to be the not category do selected so if the selected value is f then the button will be disabled I think it's just easier to see if we take a look at this how it looks visually it looks just like this to indicate there are multiple categories but of course um since I already prepared all the t-shirts but not like hoodie sweatshirts and accessories that would have been just way too much work for essentially no purpose cuz we want to learn the code and not how to design hoodies and sweatshirts and accessories in Photoshop as well and we are disabling to click on those and only allow for the t-shirts that I actually made the assets for beautiful so that's the first kind of filter I know it's more like decorative but now we're going to get to the actual um functional filters that we really care about and we are going to do that right here under the UL element and before the closing div element and we are going to do this inside of something let's go into our terminal called the accordion component by our UI Library accordion let me show you what this does basically it looks looks like this we can expand each section and that's going to expand beautifully animated and that is going to contain our filters and we're going to make use of this component for our filter so let's install it npx chat cn- UI at latest add a cordion there we go make sure to spell it correctly and then hit enter that's going to install the accordion component into or app in the same approach with all the other components into our UI folder right here under accordion. TSX and what that allows us to do is to now get started in actually using the accordion now how do we do that well let's start with the accordion component and import it from our own components folder and this accordion expects a property that is type and you can choose between multiple and single but it essentially means how many filters can you expand at the same time you see right here this is a single because only ever one section is expanded at the same time when I click a new one then the old one is cck closed but we of course want to allow multiple to be open at the same time and with a class name of animate dnone now inside of this accordion the first filter we're going to work on is the color filter so let's add a quick comment here in the jsx to just make sure that everybody who reads through this file can very easily see what we're doing is the color filter that's about to follow as the first accordion element so let's create it the accordion item right this is like kind of the well basically it corresponds to one of these sections you can see here on the right hand side that's what we are creating right now and this expects a value prop um this is going to be the name of the section and because you could for example pass a default value into the accordion which values should be default opened and that's how you identify certain sections of the accordion inside of here let's create the accordion trigger also from our own components and not from radics and this is going to get a class name of padding by 3 a text of SM a text Gray of 400 and on Hover a text Gray of 500 so a bit darker just for some hover interaction and inside of this animation trigger inside of a span element with a class name of font D medium and text Gray 900 there we go we're going to say color now let's save that let's start back up our development server and let's already see what this looks like because what we expect to happen is for an accordion to show up here on the left hand side in our filters let's refresh the page there we are and if I zoom in a bit you can see that we have the color right here and if you click it then it looks like the section is expanding because the Chevron right here the arrow is kind of flipping around as we click it but in reality of course nothing is showing there because we haven't set the content that should show when you click on the accordion trigger how do we do that well same thing as for the drop- down menu if you remember that we had the trigger and we have the content and same exact really clean API for the accordion we simply pass the accordion content in here now this content gets a class name of padding top 6 and also an animate - none and inside of this accordion content we are going to put an unordered list this will get a class name of space y4 just again for some vertical separation of the items it's just looking a bit nicer and inside of here we are going to display the color f filters now one approach that I really enjoy to kind of Define the filters that should be shown in our application is at the very top of the file just like with the sort options so what does this look like in practice how do we actually Define the filter let's say cons color uncore filters and this again all uppercase to indicate that this is a value that never changes super common convention is going to be an object and this is going to get an ID of color this is how we're going to identify the filter on the back end a name and this is what's shown to the user so we can say in like the upper it's not uppercase but where like the first letter is uppercase I guess so it looks nice to humans and then lastly the options and this is going to be an array now which options do we want to provide to filter by each option first off gets a value and that's going to be for example white right this is the actual color and we're going to use on the back end as well and the second thing we want on here is the label and this is again what's going to be shown to users so this is how we internally identify stuff through the value and this is what's shown to our users and now comes the trick we simply go into this line hold shift alt and arrow down and press the arrow down once twice Thrice and force I I don't know uh anyway so we have the white label white let's um create the beige and label will be beige with the uppercase B then we're going to go for blue label you can probably guess is going to be blue last or second to last is going to be green with the label going to be green and the very last color we're going to have in our app is going to be purple and the label of course is going to be purple as well and for the options we are going to add an as const as well to tell a typescript these options will never change now if you actually decide to add a custom color to your P page later of course that is possible Right but not by pushing into this array that's always saying with the ascon you can't push into this array but you could simply go ahead and add a new color like this if you wanted to have like a yellow for example label yellow there you go you just added a new color you know but all we're saying with the ason is a typescript that hey this array will not change um if you don't explicitly put the object in here um like I just showed you for the yellow color for example so after doing that let's finally show this filter to our users and we do that in the UN ordered list by saying colorcore filter oops filters. options. map and each option just corresponds to the color we just created where we get access to the option and also the option idx the index and can right away return some jsx from here looks a bit messy we're going to format this in a second and of course because we're in a list we also want to return An Li element at the very top level and because we're mapping over something this also needs a key which is going to be the option. value otherwise you're going to get like a react error in the console which is it's not bad right it doesn't affect runtime but it looks not nice you know and we can just avoid it so we're also going to pass a class name in here and this is going to be flex and items D Center and inside of this Li element we're going to put an input element the type is not going to be text this type is going to be checkbox and the ID of this input is going to be a dynamic kind of comp position so as a template string with the back text right here we are going to pass in the Color dash and then dynamically insert the option idx now you might be wondering Josh why are we doing this why are we passing an ID in here and you're going to see that as soon as we finish the input trust me um it's going to become very clear why we do this before we do that let's quickly finish this let's add a class name on this input and the class name is going to be a height of four with for rounded a border gray of 300 a text Indigo of 600 and on Focus colon we're going to say ring Indigo 500 there we go that's going to be the entire class name we're going to apply to our input element now why are we assigning the ID what's what's the reason behind that and the reason is that we can create a label for this specific element that's going to describe the filter that we're applying here so for example the label gets an HTML 4 and we can dynamically pass the value in that we assign as the ID right so the ID is going to be the exact same we can copy and paste it and we are telling HTML that this label will be for this exact input element because the um ID matches the HTML 4 that's why we're assigning the ID to the input now this label is going to get a class name of margin left three a text of small and also a text Gray of 600 and inside of this label we can put the option. label so the text that will be shown to the user for this um specific filter what does this look like if we save this let's expand it reload our page and if we now open this color filter section we can see all the filters show up here white beige blue green purple and the beauty of the label is that even if we click on the the name right which is the label the checkbox will still be checked so we don't have to click the checkbox itself but clicking the name of the filter as users often do is perfectly fine as well that's why the label here is so useful that's why we assigned the ID okay now the question is right what should happen when the user selects a color like logically CU right now no filters are actually applied to the product search so logically what should happen and the answer is just like with the um sorting filter right we also want to keep track of this in state so we can send it along in the request to our back end that actually determines the filters that are applied to the products right so the way we do that is by adding a field right here in our filter because that's where we are going to save this so the field we're going to add is going to be the color right and this color can either be well it can be anything this is an array of strings we're going to Define this in a second however we can definitely improve the way um we work with a state in typescript because as you can see right now we can literally pass any string in here there's no sense of type safety whatsoever if we were to pass anything as a color that's totally valid in our current setup and that's horrible we don't want that we want full type safety for the state how do we achieve that well by one certain library that we're going to use which comes in super clutch and that Library let's pause our develop Vel mment server for a second that Library we're going to use is called Zod so pmpm uh install Zod it's called Zod and this is a schema validation library that integrates extremely well with typescript and I use it in a lot of projects now inside of our source lib folder this is where we're going to create a new folder right now and call it validators because Zod is a schema validation Library that's exactly what it does it's helps us to validate certain data um shapes right you're going to see exactly what this does in a second inside of this validators folder for now let's create a new file and call this product- validator dots because that's how we will validate the shape a product has for example that is sent to our API in the back end now inside of here first thing we're going to do is import Z from Zod because this is what's going to allow us to actually make use of the library now and now you're going to see the beauty of Zod so we can Define we can say export const and then we can Define the shape of what a product should look like and that's called this the product filter validator and this is going to be equal to a z. object and this is a function that we invoke with an object inside of it and in here we Define the shape of a product which properties should a product always have and that's for example the size what is the available value values for the size well it's a z do array and in here this is going to be a z do um Z do enum so we're only allowing very certain properties that we pass into this enum right here what values do we allow for the size well we can Define that at the very top right here export const available sizes also in caps because this is going to be a constant value that never changes this is going to be an array and each um element in here these are going to be S this is going to be M and this is also going to be L these are all the values for sizes that we allow in our app and of course as constant because these won't change unless we explicitly add an element to the array like so but just so we can't call the um push method on it right without the ason we could but that should never happen um because we know this won't change now we can pass the available sizes into the z. enum which will create an enum from or available sizes array and let's Z know that these three values right here literally are the only valid values for these size this approach is beautiful because it allows us to get full type safety on the front end and enforce the types on the back end as well right for full schema validation which is really really nice that's the reason why we're doing the same thing for the color the color is going to be a z. array again a z. enum inside of here so very specific things we're allowing in here and we can simply copy and paste down the available sizes using shift alt and arrow down call it the available colors and inside of here of course we want to change the values right this is going to be um for example white this is going to be beige this is also going to be green second to last is going to be purple and then the last element is going to be blue so you recognize these colors these are the colors that we allow in our app of course again as a constant which means we now can pass them into the enum to create our product schema validator so later on when somebody sends a request to our back end we can make sure that the only colors they can pass in that request are these ones in the array and other values will be rejected right this is very very very helpful right after that we can Define the sort and this is going to be a z. inum directly this is not going to be an array but instead of course course um when you pass the sword just as a common for you you don't need to write this down um this is going to be like either s or this is going to be M or this is going to be L but of course this is not going to be an array this is either going to be none for example or this is going to be like price ascending but it's never going to be multiple at the same time so an array structure does not make sense at all here so that's why it's directly in enum and we can Define the available sorts by saying export const available uncore sort and this is going to be an array of none of price- ASC for S sending and price- thec for descending as const of course once again and then we can simply pass the available sort into the enum and we're almost done with our product filter validator because the very last thing we want to do in here is the price and the price is interesting because the price is going to be different than all the other ones like why you might be wondering let's move this into a side by side why is the price different because if we take a look at the price structure like the price filter right what are the options that you can pass for example under €20 right or dollars or whatever your currency is right under €40 and so on what does this mean what is the structure of a price it's very different than a string array a string array doesn't make sense here you can't pass or well you could pass but it doesn't make sense to pass literally under €20 as a string to the back end the back end won't know how to handle that that is why the price is actually not going to be a string but it is going to be an array of numbers and to be very specific let me get out of here hello I don't want to draw an arrow what the hell just get rid of that Arrow dude all right so anyways a price is going to be an array of exactly two numbers called a tuple for example from zero to 20 this data structure really well represents what we're trying to do with a price where each price is free from to right it's kind of a range of price or 0 to 40 if we wanted to allow products from 0 to 40 or um Z to 100 Etc you get a point right that's why the price is very different than the previous ones but defining it in Z is not harder than the previous ones right we can simply use the z. Tuple to tell Z that this is going to be like a number and then one more number so two numbers inside of an array that's what a tup ball is and we can simply pass this in Array of z. number and Z do number and invoke that as well so Zod now knows that this is a pair of two numbers and that's already it now what defining this schema does for us is basically twofold right first off enforce shape on backend we can make sure that the data we receive on the back end actually matches exactly the shape or we will reject the request so for security reasons this is very nice and then the second reason is that we get full type safety full type safety on the front end right we can now pass the generic that we can infer from the schema to the state sounds abstract I know but let me show you how this works and let's do it together it's very easy we can export a type and let's call it product State and this is going to be equal to the z. infer so this is a utility that Z provides to us to infer the type from the kind of JavaScript typescript syntax um schema and to generate a typescript type from it this takes a generic and that's going to be the type of product filter validator just like that and boom We Now get a typescript type from our um schema that we can use to validate stuff at runtime right that's really really cool and we're going to do one little change to this so for now let's mark this and cut it out because we're going to need it any a second but we're going to wrap this in an omit utility type from types script because we're going to replace one property in here because let's paste this back in and we are going to omit the price value from this why are we doing this because while all the other properties are going to represented be represented as is in our state the price is not going to be because for the price the user is going to have two options there's going to be like under €20 there's going to be under €40 there's going to be any price so the user doesn't care but the second main option is going to be a custom value and we need to keep track of whether this is currently a custom value or not and currently the price does not allow for that so we need to modify or type to be able to tell in state is this currently a custom value or not so we can um display some jsx conditionally for that right and currently in the type that is not possible so we will replace the current type and we can do that with the omit typescript utility type and remove the price and now replace it with a new property so and object this is going to be the same kind of name this is going to be the price CU that's what we're overwriting now and we're going to overwrite this with the is custom and this is going to be a Boolean value and secondly the range and this is of course going to be a number number tle as we discussed earlier right so all we're doing is telling typescript yes this will still be a tuple right same thing but now separated into a range and then is custom property that we're going to keep track of in state and that's it we can already take the product State and pass it as a generic into our react state right here to make this type safe and also of course need to import it and check what happens type empty string is not assignable to type white beige blue green purple we now get full type safety for the values we can pass in here and now of course by default we want to enable all the colors everything should be selected like as a default filter so let's pass all the values in here so beige blue green purple and white of course you could also abstract them somewhere else and then pass like something like default colors in here I don't think that's more readable though I much prefer this approach of directly passing the strings and as you can see that's not even it yet we're also expected to pass in the price with full type safety in here as well with the is custom and range that is beautiful so for the is custom by default we're going to pass false and for the range let's abstract this into a constant value right here because we're going to use it in multiple places I think this is more readable if we put it just here but I think let's define a default uncore custom undor price and this is going to be a tupal of 0 to a 100 As and we're going to cast this into a typescript type which is going to be number and number right here the reason we're doing this is if like if we're doing this right this type is a number number but if we didn't cast this then it would be a number array so typescript doesn't know it will always be too long but if we do this as constant then also the numbers themselves are going to be inferred and that's not always the case the numbers itself can change but what won't change is that this is always two numbers so we're casting it as a number number array so typescript will always know the number can change right it's not always going to be zero and the 100 but the length of the array will not change that's why we're doing this and this is going to be the custom range we pass in for the price now of course that's still not going to fix the error just yet we're also expected to pass the size array and just like with the colors we can allow all the sizes which is going to be l m and also s to be selected as the default filters and after passing those this looks super nice I really like this API design it's super readable because you can tell right away when looking at this which filters are applied by default this looks really really nice and intuitive and to actually make use of our filter right do you remember the um color filters that we're mapping over how do we now keep track of them in state and the answer is we do that in the onchange Handler of our checkbox input so we can simply pass an onchange Handler into here and this will be a function that is being executed now we could in line Define the action that should happen but because we're going to make use of the same function the same onchange Handler as well not only for the colors but the same logic will apply to the other filters like for example the um the where is it the size filter right same logic where we have an array of string values that are then being filtered right same thing for both of these we can add substract this into one function and make our code a lot cleaner that way let's call that function the apply array filter function because that's literally all it will do it will apply an array filter so this will only be applicable for array values and this is going to be an arrow function that takes some Properties or I guess parameters would be the better way to call it right and this takes two things so the category and this also takes the value so the category is going to be for example the color or the size and the value is going to be the actual color like the beige or white as a string or the S M or L as a string right that's why we're kind of differentiating between those two and we can tell typescript what these are the category is going to be of type and we're going to make use of a typescript utility type here this is going to be the key off and then the omit typescript utility type and in here we can pass the type of filter and remove both the price and also the sort property because these are not array values right if we take a look at them this is not an array of strings and this isn't either only the size and the color are and this function the apply array filter is specifically for array values and by the way I completely messed up the syntax here of course this is not going to be inside of the function body right this a typescript definition so let's cut this out using contr X and put it right here where it belongs where we can tell typescript the type of the parameters that we expect here and then the value as I said is going to be string like this will be for example the s or the M for the size or this could also be like the white or beige and uh all the other colors that we allow so you get an idea of what this is right and the logic that's going to happen inside this apply array filter is pretty straightforward cuz if we imagine what a filter looks like in state right this is basically an array of white and and let's say for example beige right let's let's say we allow two colors right here this is our current filter then imagine the case where the box with the value of uh not purle but purple is now checked right this filter is now being applied what's the logic that should happen in the apply array filter first off we want to check is purple already in the array because if purple is already in here and the purple filter is checked right if it's already checked let me show you that right here and we check it again it will be unchecked and therefore we want to remove this element from the array of course right and if purple is not checked and then we check it or I guess purple would be here and then we check it then it should be added to the array right so all the logic that this apply array filter function handles is to check of is the element already in the array and if it is then remove it or if it's not in the array then we add it that's literally all this apply array filter does and the logic in code is as straightforward as that explanation first off we do a check is the value already in the array is purple already in the array right so let's call this the is filter applied and this is going to be the filter at the category so this going to be like uh color or size for example do includes and then the value as never right this as never is just a typescript thing if we didn't do this we would get some weird typescript error that really does not matter typescript doesn't understand what's going on here so this is totally fine to add and all the is filter apply does for us now it tells us is the value already in the array or not does the array already include the value and if it does right if is filter applied that means we should remove it from the array because we're unchecking that filter so we're going to set the filter to whatever it was previously that's what this means we spread in the previous values of the filter and then we change the category and we do that by putting it in angled brackets the category in here so this is equal to for example the color right or this could be the size but it is dynamic to whatever is passed into the function right because we can't hard code the category here that would literally search for the string of category in our filter which doesn't exist we only have color price size and sort there is no category in here right that doesn't work but instead we mean the color or the size dynamically and we do that using this syntax right here by putting it in angled brackets and in here we're going to say the previous category dot filter and for each value for each V that we have in here we're going to filter out the value by checking if the V is not equal to the value so basically maybe a bit abstract if you're not too familiar with how react immutability works but essentially if for example we are um applying the color category right if the category is color then we're setting the value of color to the entire array that it was previously right the previous at the category but we only filter out the elements that match or current term which would be purple so we're setting the same array to what it was previously but without the purple element that's how we changed the state immutably that's how you kind of work with state in react and then the opposite should happen if the element is not already in there right if we check the purple box and it's not in the array then this should be a filter that is now applied right so if purple is not checked and now we check it then now we also want to show purple products the way we do that is by now putting it in the array in this if statement right so in the other case where the filter is not already applied we're going to add the element by setting the filter to whatever it was previously right so we spread in the previous values and again dynamically at the category like size or color we basically now want to add that property into the state how do we do that by setting the category to whatever it was previously right we spread that in the previous at the index of category and then at the last as the last element of the array we append the value so all we're doing is taking the original kind of array that was there and now adding the purple value value to or state at the very last index of the array if it wasn't applied before right that's what the if check is for so now it will be in our array and what this implementation of apply array filter now does for us is really beautiful because as we go down to where we want to apply the filter in the onchange Handler we can simply call the apply array filter function that we have just defined and pass two things the category which is going to be color in this case right because we're in the color filters right here uh uh uh right here color filters and then this is also going to be the value the option Dov value and the entire logic of is the element already in the array or not etc etc is being handled by our function and we don't need to worry about it here and lastly to turn this into a controlled input we can simply pass in the checked property which determines if the checkbox is marked or not and this is going to be the filter do color do includes and then the option do Valu so if the current state includes for example purple then yes the checkbox should be checked and if it does not include the color then it should be unchecked that's all we're saying here and let's just demonstrate this right let's log out our filter state so we can see what happens right let's conso loog the filter format or file save or file and then head over and actually see this in action let's turn this into full screen go into our console let's reload this really oh and of course well we should start up our development server fur this to even work let's say yarn Dev let that start and then let's take a look at what happens in the console cuz now we should see our filter and always the correct values applied for the color filter that we've created so let's let this load it seems like the server has already started let's refresh the page and in nexts this sometimes takes really really long especially like the first loading once you start up the server God damn all right there we are okay let's clear the console and again this takes a this takes so long man let's refresh this there we go okay we get an error right here what's the problem each child in a list should have a unique key prop okay we're going to fix that in a second no problem right now as we select or unselect the white for example we should see the color array not contain white anymore and if we recheck it then white should be back there we go in the array beautiful so if we uncheck like four out of the five things then only purple should be left in the array let's check if that works color only purple beautiful this state is now ready to be sent to our API at least the color one right we didn't implement the size one yet as you can probably see there is no size option in our filters but the logic for everything is now in place and this is a really really nice readable maintainable API design okay that looks very very nice let's close out of the console and scroll down to the color filter we just implemented because the thing is the color filter really is the entire logic behind the other filters as well now the price filter will be slightly different because if you remember it involves like this custom kind of price range right that the other filters don't have so the price filter while it will still built on top of the logic of the other filters the price filter is the only one that's going to be a bit different but for example what's really really similar to the color filter is as you can probably imagine the size filter because it's also just an array of strings right so what we can do is go ahead and mark the entire color filter accordion item using our EMT outward like balance outward hotkey that I showed you in the very beginning which isrl M or you can also just manually mark it if you want then we can copy and paste this down using shift alt and arrow Down Bam there we go as easy as that it's kind of separate this and let's also add a little comment for us in the jsx to know what this is so these are going to be the size filters now now what changes in the size filters let's open up the page and close out of that of course for one the um name right this is not color anymore this is going to be size pretty obvious and then in the accordion content instead of the color filters we're going to map over these size filters however if you might um as you might might have noticed they don't exist yet but they're also going to be super similar to the um color filters so what we can do for example is using shift alent arrow down also copy and paste the color filters and change the name to be size filters and then of course adjust them as we need and I really like this approach of going about things because at the very top of the file you have all the filters listed and grouped together if you wanted you could even um put them in a separate file but I prefer them to have their right here and then you can always just add a filter anything you want as easy as kind of copy and pasting this down and I just really like this approach of going about things so the ID of the size filter is going to be size and the name we're going to display to users is going to be size as for the options we're only going to have three options and that's going to be the value s with the label of s the value M with the label of M and lastly the value L with of course you can probably guess the lab of L and this is also going to be a constant but the entire object is going to be a constant not just the array so we're going to move the S constant from from here over to here now with these size filters I think we are already kind of set right let's go down to the color filters they are right here and as we scroll down we get to the size filters that we just created and we can replace the color filters that we were mapping over with the size filters now what change we getting an error down here for example and that's awesome because the error should happen because argument of type smrl is not assignable parameter of type white beige blue basically any color right so typescript is telling us hey you didn't properly adjust this to be the size filters this is still for the color filters for example in the uh ID right we need to change this to size this is not for the colors anymore but the input as well as the label are now for the size filter and same thing here we're not um in the filter.com anymore but of course the filter dot size as easy as that we can simply change this same thing here for the category completely type save how nice is that change it to the size and there we go that's literally all the changes we need to do let's hit save let's pull up the website and this is not a proper side by side there we go and there are or size filters just like that however one thing very apparently still doesn't work and that is right here the accordion item value because so what happens right now if you try this out if you close the color you also close the size and vice versa right this should not happen and that's because both of these are still marked as value color so we need to change this to size and then hit save and we are golden so now we can if we restart the page separately expand the individual filters and then select the product spaced on it and if we take a look at our console because of the um function we just wrote the array logic function for the State updates we're going to see as we hit them the state is updated and the correct size is right here in the array and in a perfect state to be sent to our back and for actual data fetching very very very nice now let's do this one more time for the most advanced filter which is the pricing filter because it involves a custom option so let's mark the entire size filter section and once again using shift alt and arrow down copy and paste it down once there we go we can also just comment this as being the price filter just for us so it's easy to kind of see what's happening here now of course the value first thing we're going to change to price same thing as the label on the accordion trigger what you press to expand the filter right that should be price as well and just like with the size filters we also want price filters where we Define which filters are available for the price now the basic structure is going to be similar let me show you what that means let's define a con price and then underscore filters and this is going to be an object and we can already mark this as const um for later so we don't need to remember to do that now the ID is going to be price and of course this will also get a name just like the other filters like the size right here for example this is going to be price now the options are going to be in Array just like the other filters just like this of objects where each object gets a value and a lay table but the value for example in the first object that we're going to create is not a string like in the in the other filters right instead the value as we talked about earlier is going to be a tle a array that has exactly two members inside of it so the value for the first option will be one to a 100 so this will be a product price of $0 or did I say one this should be zero right um so0 to 100 um so a product price of $0 to1 $100 that's what it means and then as the label we can choose for example any price so because no product in our catalog is more expensive than 100 dollars Euros whatever you want uh as the currency this will include all the products we have in our shop now second option is going to get a value of 0 to 20 and then as the label we're going to say under €2 or dollars whichever currency you prefer now we can mark this copy and paste it down once using shift alt and arrow down and change the 20 to A40 so as easy as this we just created one more step in our pricing filter and the last option as you saw in the very intro of this video is going to be custom right where you have like the slider to customize the option yourself and that will actually be defined in the jsx and not inside of the price filters just doesn't make a lot of sense to Define it here so the custom option defined in jsx we can just add a little comment here that explains where to find the implementation for this that's going to make it very easy for anyone kind of looking through our code to see how it was being written so once we scroll down to the actual price filter the entire first part right is going to be very very similar so we're still mapping over the price filters and why does this give us an error did you mean op oh did I make a typo yes this should be options in the plural there we go and now the error down here should be gone yes it is perfect okay so we're still going to do pretty much the same thing but the key is going to change to be the label because a key cannot be the option. value as a read only tle right that doesn't make sense as the key so we're going to use the label of course we need to change both the ID and also the HTML form so so we can change this to price and what's fundamentally going to change is the onchange Handler because this is not an array right we don't need to apply the same thing that works for the array in fact we just can't instead we're going to set the state here manually so we're going to set the filter State and we can directly implicitly return an object in this syntax right here by wrapping the function Cur braces inside of parenthesis now before we write the actual logic we need to receive the previous state that react gives us X system and we can simply spread in the previous state and only update the price State now as you might know the price has two properties the is custom and range and these are going to be representing the options that are non-custom so if you look at the price filters these are non-custom these are pre-chosen options this is not the slider where you can enter a custom range so the is custom property is going to be set to False only when you use the slider to define the price then it is going to be true and the range is going to be in Array where we spread in the option. value which is basically the tupal right why are we not using this directly because typescript basically complains that hey you can't use a read only array here um so technically codewise this is totally fine no problem at all but just typescript will complain and to make typescript well happy we can simply use an array and just spread it in so it's basically a clone of the values that is not read only anymore really just a typescript thing and now as for the logic to when this should be checked or not so first off when should this be checked well if this is not a custom value so if the if not filter do price do is custom because if we have a custom value then there's no way this could be checked right so basically there's going to and why does this keep switching away from a side by side anyways basically there's going to be four options right there's going to be any price like this there is going to be sub20 so under 20 that's going to be the second option then there's going to be under 40 and then there's going to be the custom option right so custom and custom will then contain the slider that lets you set um a custom price range for example from here to here right and if the custom option is checked so if there's an X here then these cannot be checked only one option can be checked at the same time otherwise this would not make sense and if this is checked then these custom options so the first three we're rendering out here and will never be checked so that's the first precondition we're checking for and if that's the case if we can go further then we're going to do one more check and that is if the filter. price. range at the index of zero so the first number in the Tuple is triple equal to the option. value at the index of zero and also right and also same thing for the second number of the Tuple if the filter. price. range at the index of one so the second number is triple equal to the option. value at the index of one so basically all this is saying if the current filter has exactly the range from this to this then well the option will be checked so if the filter goes from 0 to 20 and the option goes from 0 to 20 then this box will be checked so it would be this one or it would be this one from uh 0 to 40 and I think this is pretty intuitive and makes sense so we can leave it at that that's literally all the logic except for one thing that we want to change and that is from the type checkbox which actually allows multiple fields to be checked at the same time which is definitely not what we want for the price only one of these options can logically ever be like at the same time that's why we're going to change this from a checkbox to a radio just like that and hit save and if we take a look at our website and what happens we're going to see one more section pop up here that's the price and we can see any price under €20 and under €40 very very nice let's see what happens when we click them and open up the console let's open this up let's hit under 20 and we should be able to see the price is custom false perfect cuz it's not custom the range is 0 to 20 perfect let's check if this works with 0 to 40 let's open up the price and it do we can see the range of the price is from 0 to 40 perfect last thing to do now for the price is to add this custom kind of option right and I guess that's that's the most advanced part of this filter right um because it involves two parts not only the radio button but also the slider that lets the user choose um from where to where they want the prices to show and the the thing is if we look at how this should look like this option is nothing else than just another Li element like all of these before as well therefore in code it's just going to live still in the UL before all the closing stuff right here um just below the previous Li element so it's just another Li right and it gets a class name of flex a just ay- Center a flex D call and a gap of two and inside of this Li element we're going to put a div it's not going to get any class name and inside of this div we're going to put an input component and we could write this out yourself but it is going to be very similar to the previous input so let's just grab this along with the label and kind of paste it below because after all they should look the same right this entry right here should look the exact same as the previous ones so just make sense to use the same code the same styles for them as well now what we're currently building is this one right here just for your understanding this box is the input we just copy and pasted now what should happen on change once you select the custom kind of option what I think should happen is that the range is being set from zero to 100 as the default so once you click this option the slider should always default to some value and I think from 0o to 100 so the entire like product price range is a pretty good starting point and as the um as the idea for this this we're going to use the price but not the option index cuz we're not mapping over anything but the price uncore filters do options. length because in any case this will be the last option that is available to users so it makes sense to use the array length to dynamically determine how long that should be and then of course also use it for the HTML 4 and then as the label let's choose not customer but custom so the user knows what's up and the check if this is checked is going to be incredibly straightforward this is just going to be the filter do price do is custom so whenever we selected a custom value then this checkbox will be checked and because like to make that happen of course we need to change the false to a true so whenever you click this check this checkbox right here the is custom will be set to true the range will be set to the default values kind of 0 to 100 the entire price range and then you can actually worry about the slider before we go about implementing the slider let see how this looks like currently can see the price we can see the custom option great right what we expect to happen in the console right now if we select that option let's get rid of all the arrows we're going to fix them later is for the price range to be custom perfect that works and to be from 0 to 8 100 which it also is very very nice that's exactly what we expect and that is the ideal Foundation um for us to now build on top of for the slider before we do that let's create create a div right here below the closing div under the label with a Li and one UL to go this div is going to get a class name of flex and justify between and inside of this sff let's open this up goes A P tag that says price and it's going to get a class name of font D medium now right below this pag let's create a div and inside of this div no class Name by the way we will put something Dynamic what is that going to be well it's going to be a check if the filter do price do is custom so if the user is currently choosing a custom value in that case we want to show the lower end of the price because what we're doing right now let's visualize this so we understand why we're doing what we're doing basically above the slider there's going to be some text it's going to be saying price right here oh and this is already small okay that's fine it's going to be the price and then it will St like from zero to 00 right that's how this should look like and the number right here that we display on the left hand side this one should always be the lower end of the price range right and the way we determine that is pretty straightforward for example we can call it the Min price so let's quickly go ahead above or jsx and determine this so the con Min price the price we can show on the left hand side right here the lower price from the custom range is going to be equal to the MAF do Min so the minimum value of any amount of numbers that we put in here like this for example this would return the one because it's the smaller number of the two and instead of a hardcoded one and five we're going to put in the filter. price. range at the index of zero and also the filter. price. range at the index of one right so the upper and the lower bound and then the Min price will always be the lower bound of those two same thing for the max price right just the other way around instead of math. Min we can simply use math. Max and as easy as that we now always have the upper bound that we can show um on the right hand side right here how cool is that so we can now go down in our jsx to the diff we were just editing and if we have a custom price if the user is setting something custom in that case you want to show the Min price do to fixed at zero so this will not show anything behind the comma and in the other case where the price is not custom we want to show the filter. price. range at the index of zero and also do to fixed there we go uh to zero so we don't show anything after the comma and we can also use the euro symbol here or dollar or any other currency that you want right so this will be the first number right here let's separate this option from the next option using a JavaScript slash so we can ensure there's always going to be space here and essentially we're going to do the same check again just for the upper bound now so the filter. price. is custom if that's true and that case we're going to show the max price. to fixed of zero and in the other case we're going to render out the filter. price do oops do price. range and you can probably guess now whereas this was the first number this is now the second number so at the index of one do to fixed also to zero let's save this and already take a look at what this looks like right that was kind of an abstract step so let's pause here and see what we did there we go that's what we did this zos to a 100 and we kind of forgot the um currency symbol so let's add that in looks much better with one there we go0 to a 100 very very nice the last thing that is missing now is the actual slider that now lets us control that custom price range this slider and if you're thinking like wow this will be a very complicated step actually it won't let's go into our terminal and I'm going to show you why let's clear the screen and the reason why is because UI Library already has that fully accessible working slider beautiful that we can make very tiny changes to to make it suit or use case for example adding another thumb so we have like two um things we can drag around on the slider to create a range the way we install it is by using npx shat cn- at latest at slider hit enter that's going to install the slider for us and once that's done you're going to see it's actually very very easy to use in our application perfect it's done we can already start back up our development server and then let's go down um right here and then with one closing div two closing div but still in the LI element let's use the slider we just created from our custom components and not from radic right this slider can be self-closing and what we're going to pass in here first off is a class name now this class name is going to be dynamic right with our CN helper function and we want to pass a condition class name that's why we're using it and that conditional class name is going to be opacity 50 and we're only going to apply this if not filter. price. is custom so if any other thing is checked right and let's restart the page you can see what this means if any other previous option is checked then the slider will appear as if it's not usable because it's not um so for example ah dude this takes really long to load I want to show you what I mean there we go finally if any other option is checked then the slider is kind of grayed out and only if we select the custom one then it's actually fully visible and we can interact with it right that's the style we want to go for very very nice and we can do that using this conditional class name on the slider now of course we actually also want to disable the slider um right now it just looks like it's disabled but we actually want to disable it so we can pass that SD disabled prop and this is going to be same condition not filter do price dot is custom so if you selected any previous option just like before the filter will H the slider will actually be disabled now what should happen on value change when the person selects um an option in or slider by dragging around the thing right wa before let's add the second thumb because right now you can't do AR range with this and the fix is actually really really easy uh we can just go into our slider component and copy and paste the slider primitive. thumb we can mark it hold shift alt and arrow down and kind of uh have two thumbs in here and as you're going to see that's going to create a slider with two thumbs and why does that not maybe there are two thumbs but we still need to set the value okay so let's continue for a second and I think that might automatically fix itself and because that is the change we need to do here just add one more thumb of the same and then let's go down back to our slider and um we can already Define the value let's get to the on value change in a second let's delay it for a bit let's already give this a value to see if the two thumbs actually work so the value is going to be either if the filter. price do is custom if we want to set a custom value using the slider then this is going to be the filter. price. range if we don't want to um use a custom value with a slider then we're going to set it as the default default custom price and if you remember the default custom price if we go to that is an array so it's zero and 100 so the zero will apply to the first Thumb in our slider this one and the value 100 will apply to the second thumb just as easy as that that's why we have an array structure here you could also do this with like three thumbs probably but that doesn't make a whole lot of sense but you get the point right that's what the value means and it's always kind of arrange a tuple um of two numbers that's how it works we can also pass this a minimum value for example the default price uh the default custom price at the index of zero which is the number zero right but um I mean we could hard code it but I think it's better if we ever change it to have it um all in the same place the default value is going to be or default custom price so the 0 to 100 range we set above and then the max value this this slider can take on is going to be the default custom price at the index of one which corresponds to the number 100 but if we ever change it this will automatically change with it and then we can also give it a step for example of five because rarely are you going to need like $1 different steps in the actual slider but we can just set it to five um or to 10 or to whatever you want basically if you're selling very high ticket products this might be higher if you're selling like dollar products this might be lower like 50 Cent like 0.5 for example um but I think five works pretty well for our app let's comment out the on value change for a second and see if the two thumbs actually show up and because they should so let's restart the page and yes there they are there are our two thumbs and if we choose the custom range then we can see that our slider becomes visible that we can interact with it but we can't drag around the thumbs yet and the reason is because we hardcoded the value but didn't give it an onv value change so it will always be the 1 to a 100 and we literally cannot change it as is that is why the onv value change we receive something called the range which corresponds to how many sliders or thumbs we have in the slider so in our case this is going to be two so in our case we can right away destructure those the new minimum and the new Max right from the range we can use array D structuring syntax to get access to those two so whenever the user changes the thumbs and slides them around we're going to have access to the new values just like this and can set them to a filter state so we can say set filter to anything it was previously we can simply get access to that once again you probably know the drill by now by spreading in all the previous options and only changing the price and now of course the value we're going to set here is for the is custom true because it's literally the custom slider right it just makes sense to set it to True here and then for the range we're going to save from the the new minimum to the new maximum just like this now if you're wondering why don't use the range right here um in the like directly well that's because typescript will complain typescript thing once again you know yes this would be perfectly valid syntax this would work um but you would have to use something like at TS um ignore and I really try to avoid this and not even this works maybe we should set it here yeah this would work but I don't like to use TS ignore I think this is just a much um nicer way of going things same code um but no typescript suppression you know so I think this is just a better way while still achieving same functionality as I said right and if we save that then we should notice that we can now actually slide around the price so let's try it out let's go for custom and yes this works very very nice and even if we try to drag the second thumb before the first one we always have the lower bouns on the left and the upper bounce on the right hand side so no user can make mess this up very very nice okay so let's actually change the product results based on our filter I mean dude we're literally keeping track of everything so nicely how about we finally send that to our API and then do some performance um improvements and better error handling um so right now we can see the price range would be 10 to 80 perfect if we set this down then the price range will be 0 to 80 just as we said it or like this and it should be 0 to 40 let's verify that very very nice and also the is custom property is now false perfect okay so we keep track of all the filters one thing you're going to notice is while we drag this around there's a lot of State updates this is something we're going to fix by the way by De bouncing the state we're going to get to that first let's make our product's results actually um change by sending along the filters with or API request up here so we're already sending the sort filter perfect so what do we need to add we still need to add the color which is going to be the filter do color we need to add the price which is going to be the filter. price and lastly we need to add the size which is going to be the filter do size and that's all we need that's what we're going to send along to our API request where we now have all the data we need in our route dots that we already created under API products and to kind of process that request and get access to the data so how do we get access to the data in the first place what we can say the cons body the request body in this post request will be the await wreck and the wreck is just the request that we get access to right here which is going to be of type in next request by the way and we can simply call the await rec. Json this is an asynchronous operation that's going to convert the request body that is sent along into a Json format assuming the content type is application Json which in our case it is now comes a really cool step you remember that product validator that we wrote to kind of base or type on uh in the front end the cool thing is we can use the same validator to validate the incoming data in our back end as well the way we do that is by using the product filter validator right here in our backend do pars method on it and in here we can simply pass the body. filter so what we just defined on the front end that we send along right here as an object we now parse using our schema validation library in the back end to make sure that the request only contains the data and exactly the type of data that we expect super super cool for security and also for type safety because this literally gives us full type safety of what we receive because if the incoming data doesn't match or schema then the request will be rejected automatically perfect so we can destructure the color the price the size and the sort in or backend full type safety right how cool is that and we can now actually apply the filter um in our back end now the way in which you construct the filter mechanism is going to be a bit different for each database that you're using the database that we're using in this video expects a format like this where we have the color is equal to for example white or right it goes into the same string or the color is equal to beige for example and then we can join that together with other conditions like where the size is equal to um L for example so in this synx right here we would only allow beige and white products like this and products that are in the size of L for example so again the syntax kind of differs this is the syntax that we need for our application and I'm going to show you a very very neat way of implementing Justice and we do this using our own class and let's call it filter if you've never worked with a class in Java or typescript don't worry this is going to be a very very simple implementation um but I really like this API design that I want to show you so the only value that we're going to keep track of in this class is going to be a private value that's going to be the filters the reason we're making this private is because we don't want to access that later where we use this filter and this is just an internal value for the class so we can mark it as private this is going to be a map and this map we can pass a generic for typescript is going to contain a string that maps to a string array which are the conditions so this could be for example color and this could be white beige and purple as the string array in here and we're going to default this to a new map just like this very very nice now we are only going to have three different or four different methods on this class first one is going to be has filters which is just going to check do we have any kind of filter at all so the logic is very very simple we're going to return this do filters do size is larger than zero so do we have any kind of filter applied that's going to be a check we're going to need later second function is going to be add how do we add a filter this is going to take a key which is going to be a string an operator so for example is equal to larger than smaller than that's the operator also of type string and lastly a value which is going to be either a string so for example l or M or s or the color like white or bash or this is going to be a number like the price for example like 100 in in our case Euros or dollars or whatever you want right so inside of this add function let's get to the actual logic and if we add a filter all we're doing is pushing into the string array in our map right that's literally all this is so we can say the con current filter or just filter we're going to call it but it is the current filter that is applied is equal to this do filters. getet the key so for example if we already have a color filter applied with white and beige and we now want to add purple then filter is going to contain the previous values now if the filter doesn't exist let's also put this or as an empty array so we can push into an empty array instead to avoid the weird edge cases and then we can say filter do push and we can push a string into here so this is going to be a template string and first we're going to put the key so for example the size then the operator for example equal to so for example let's comma this in this part we're writing right here could be for example the color right as the key that the operator is equal to and lastly will be a check right if the type of value is triple equal to number in that case we're going to put the value directly and else we're going to put another template string in here where we wrap the value in quotation marks and then the value goes in here so basically all this is is if the value is a string then we're going to wrap this in quotation marks like where color equals to white but if this right here is a number like the price right if the operator is price is equal to 120 then we don't want to wrap it in quotation marks that's why we do this check right here in that case we want to list the number directly without the quotation marks around it right that's what the check is for and then lastly we're going to say this do filters do set the key to the filter we just created right up here um so we're kind of adding on to whatever we had in the map previously by the item that we just added and that's all the logic there is we can also Define a function that is the add Raw in any case where you want to do some custom stuff have more flexibility than just the add function allows you can add a raw filter if you want that consists of a key that is a string and a raw filter that is also going to be a string we're also going to make use of this so you can see what the implementation looks like and the logic is very simple we're just going to set the this do filters and then do Set uh where the key is to the raw filter but inside of an array so it's going to be the only element in the array but of course we are expecting an array of strings so we couldn't just pass the raw filter because this is just a string and we expect an array to be in our map perfect last thing we're going to add on the filter class we're almost done by the way is going to be the G function because right now we are going to have a bunch of properties like color this maps to for example white and also to beige and imagine we also have the size in our map which maps to like l or also to s for example right this would be our map up here now we construct the actual search string because do you remember what I told you which format we expect here it's something like um color is equal to White and also the uh size is equal to l for example so we need to convert this map into this kind of syntax and that's what we do in the get function right here to do this let's first Define an empty array and let's call it parts so the con parts parts are going to be a string array and we're going to default them to an empty array so we can now push into them by the way why do we still have the console open here we don't even need that anyways let's now go through each filter and join each thing together using or so for example if we had multiple colors then it should look like this right where we join them together with the or operator because any single color of these is totally fine so we're going to go through each filter so this do filters do for each filter we are going to run the following function so first off we're going to say cons grouped values is going to be equal to filter. jooin and we're going to join them together with the or operators separated by spaces so basically we're taking each value that is in the certain filter that we're going through and joining them together with or so we transform them into this kind of syntax right here and then the overarching ones are going to be joined together with and so we apply them as well so how do we do that well first off we need to push this into the parts so let's say parts. push and what are we going to push well inside of a template string in parentheses just like this we want to push in these group values which is essentially like this first part right so only the colors but all of the colors together we're going to push into them in parentheses right these you can see right here that's what we're doing and let's put the grouped values right in here and push them into the parts and then all of the individual Parts like this and this are going to be joined together with the and operator so we can simply say return parts. join and join them using together and join them using the end operator that's what I meant to say beautiful and that's literally the entire filter logic done what we can do Now API design wise looks very very nice in my opinion so we can say the const filter in order actual HTTP Handler is going to be a new filter and instantiate that now look at this we can say color do for each color that is passed into this API route we can simply call the filter. ADD and now pass three things the color which is the um category that we're going through then the operator which is going to be and and lastly the value which is going to be the color we get from our front end like white beige green purple and so on so we set this filter and they will automatically be joined together like this in the filter class we don't even have to worry about it how cool is that same thing shift alt arrow down for the size so size dot for each and by the way if you wondered how a mark two things here at the same time Mark the first one and then hit contrl D and that's going to mark multiple of them and we can also Mark the next two so hit contr D two more times and just change them all to size as easy as that right there that's why I really like this filter implementation and lastly for the price we can call the filter. add raw function that we defined above so this is going to be the price filter and as for the value that we're going to add here this is going to be a template string where the first price is larger or equal than in Dynamic the price at the index of zero so the lower bound that is passed to this API request and also where the price is smaller or less than equal the price at the index of one so the um upper bound of the price range right so realistically this would be like price larger than zero and price smaller than a 100 that's how we enforce or filter in the database level and if you're now curious what we're doing with the ha filter which we haven't used yet that's what comes in clutch right now because in our database request we can add a filter and this filter is going to be if we have a filter if filter dot has filters in that case we want to pass the filters here so filter. getet right but if we are not if we don't have any filters then we also don't want to apply any so we're just going to pass undefined in that case so if we have filters we're going to use them if we don't have filters we're not going to use any because of course we can't but we also don't want to pass an empty string here so that's just how we tell our database that don't apply any filters in that case perfect and that's literally almost it the last filter we haven't used yet is the sort filter so either non price ascending or Price descending and the way we do this is actually really cool and it lies in the nature of a vector database which by default queries the most related vectors and if you remember what a vector means a product Vector when we set that for example in our seing script right here the vector corresponds to the product color the product size and the product price as the third number in the vector so that means when we set the third number in the vector to something like a 100 right and save that you're going to see if we refresh the page then it's going to list the most expensive products at the very top because that those are the most related to this Vector right here that has 100 and why does this take so long to load oh and it looks like we get an error so what's the problem let's take a look um expected array received object in valid type where did that happen so we passed something invalid path price expected array received object so it seems like what did we do what did we pass to this let's debug this together because I think the problem lies right here the price because chances are yes this is the problem we need to pass the price. range right here so right now we pass the entire object of is custom and range but all that matters to the API right is just the range from 0 to 100 the API doesn't care about the um is custom value or component does of course to handle the logic but our API doesn't so we just need to pass the range right here and transmit that to our API route and that should work let's restart the page and hopefully everything should go smoothly very very nice it did you can see the page successfully um compiled now and we have the most expensive product first which is because we changed this vector um to a00 so the highest product price if we changed this back to zero it would be the exact opposite well the cheapest products would be listed first so that's how we do the Sorting in a vector database how cool is that right so we can basically change this number and that's going to determine the order in which we sort if you use any other database like pogress or MySQL you would of course use the order by in that case right in a vector database it just works a bit different so what we can do is do a check if these sort is none right then we're going to use the average productor price as the you know like default to search by and this is a magic number so again a constant that we always declare in all uppercase and declare above where we use it or in a central file if you prefer that so for example the cons cons AVG product price is going to be equal to 25 that's like about the average of our products in the store um of course in a real word scenario you could actually get the average from your database calculate that um by aggregation and whatever but this is totally cool for our use case right here so 25 is just the about average product price and else if we have a sort if something is passed so if the user has either selected the sort by ascending or descending up here right low to high or high to low then first off you need to check which one did they check so if the sort is triple equal to price ascending in that case we are going to default the price to zero so to list the products from cheapest to most expensive and in the other case we're going to use the max product and price also as a constant that we declare right below the average product price so for example we could um say cons Max oops product price is going to be equal to something like 50 in our case I think that is the maximum at $49.99 so using 50 works pretty well well let's format this so it looks much nicer there we go that is our sorting logic let's play around with it let's see if that already Works let's refresh the page and let's sort low to high for example let's hit that and it seems like oh yes we forgot one thing one small thing so it doesn't work yet and we're going to fix that in a second right before that that we're almost done in this API route I promise one really really good idea is taking this entire HTTP Handler and hitting control contr X to cut it and then wrapping it all inside of a tri catch block where we catch the arrow and then can handle it um so this is much better error handling and pasting that back in in the uh Tri block and for example the error handling could involve um I logging the error to Sentry right log error to Sentry uh which we use um which I've used a lot of at work so this is a tool you're going to see a lot in real life in Enterprises um to kind of see how errors happen why they happen in our case we can just say console. error and log out the error so in the runtime logs we would see this error um appear and we're also going to return a new response from this API rout where we say json. stringify and for example and in the message we sent back we can say something along the lines of you know message internal server error of course you should do like certain checks on the error if like error in of error then whatever whatever and but this in our case is totally fine right here and then as the status for this we can send back status 500 to indicate the internal server error okay little cut there cuz for me it is the next day but we are going to continue right where we left off um after improving the error handling right here in all route. Ts okay so one thing you might notice right is currently in our app if we change the filters then we keep track of the actual values in state but the products don't update so if we go to our console you can see the state is correct for example the colors right we're only allowing blue green and purple right now because white and beig are unticked it's a bit small but you might be able to see it um but the products are still like white and beige right so the products don't actually change how do we change that well how do we make it right and the answer is that's going to happen right here in our page. TSX because why we are making the changes to the state for example through applying the array filter we are never actually refetch the data and that could not be easier with react query as well because for example what we can do is simply get the refetch function from react query by destructuring it from or use Query right here and what that does for us right is now we can define an onsubmit function let's say const on submit and this is just going to be equal to an error function that simply calls the refetch method now the reason we're defining this as a separate function instead of just calling the refetch itself is because if we ever want to change the submission logic then it's going to be much more straightforward in this approach than calling the refetch directly across or entire component it's just a bit nicer a bit more readable but it doesn't make like an insane difference and this onsubmit we can now actually use wherever we change the M state right to actually go ahead and refetch the data for for example that's going to be at the very bottom of our apply array filter function and now if we save that and head back over to our application right here reload the page and change any array value for example exclude the white right here then we should be able to see that the colors are excluded but there is a small problem here it seems interesting so right now when you untake the white nothing happens but when you untake the beige then the white gets removed I think this is a pretty easy fix but let's let's take a look at the network tab CU I'm just interested in what happens here let's go to the color let's untake this interesting so the request is made to our back end what does it contain it contains the let's see as the payload sort non color beige blue green purple white so interesting so the state is still included in our request so the old state that we really don't want to have in there um is still in included okay interesting I'm going to take a second uh to see why this is happening oh of course it makes sense that this happens because of the logic right here so that's one of the main kind of hurdles you have to get through when understanding react State I bet most of you already know this right so I'm not going to go too much into detail but essentially State updates are not synchronous so once these run once this if statement is done right the onsubmit will already make the request to our network but the state doesn't update yet like it hasn't updated yet it will a few milliseconds probably after this call but because we fire the call and then the State updates the state will not be included in or request to the back end now there are multiple solutions to this right the easiest one I guess would be and and you don't need to follow along right now I'm just demonstrating this um the easiest way to go around this would be in something like a use effect where every time the state changes so the filter State updates in here we then fire or onsubmit call right this would work let's save this and let's see what happens if I was right in my theory that this is the problem let's go ahead give this a bit more space and untake the white there we go that fixed the problem okay interesting so I don't like this approach I think there's a better way because what we are going to do anyways what I teased in the very beginning of the video do you remember when we talked about this price slider right here right where every time we change this we don't want to make a request or network but we want to debounce this input which is is really nice for performance so if you change this all the time and then stop for a interval like 400 milliseconds for example only then will the request to our back end actually be made that's called debouncing it's a really really useful technique in a lot of scenarios and that will also fix the problem that we have right here now how do we debounce this and actually this is extremely easy let's say pnpm install low dash. debounce now this is a utility package that that helps us achieve this you can definitely do this yourself as well the code will just be a bit longer um without this package right it's just a small utility we can use makes debouncing super easy but honestly debouncing yourself is not a rocket science in any way either anyways we're going to use this small little helper here and we're going to say at the very top of the file we can simply import uh the debounce from low dash. debounce the package we have just installed and by the way because this is not a native typescript package you also want to say pnpm install minus d as a development dependency the add types low dash. debound so that's going to be the typescript types that are going to help us use this and actually resolve this error so typescript knows what the hell is going on here and that's all we need to do to get started in debouncing and the actual logic to handle debouncing um is pretty straightforward so what we're going to do is declare two things right here the first one is going to be the con debounced submit so basically you can call the debounced submit later as much as you want um but the function is only going to get called after that interval that we set and this is going to be equal to debound the one from low dash. debound and now the function that we want to debounce it's the onsubmit function right up here this one and then the interval so for example if you completely spam this function by changing the slider around like this we're completely spam invoking the function function only then after a break of 400 milliseconds of not moving the slider only then will the fire function will the function fire once that's exactly what we're saying right here and to make this work in react I want to show you why we yeah I'm just going to show this to you why can't we use this as is because there's a problem with this implementation that's easy to fix but I want to show you what happens so let's go down to the slider right where we change the the sliding logic right here in the onv value change and whenever we changed the slider we are going to invoke the debound submit and let's see the problem in this implementation so let's open the network Tab and we're going to see if we change around the slider oh and we of course need to restart or development server let's go into full screen here and let's let this reload because what you're going to see is that the debouncing it kind of works but not as you expect there's a a very fundamental problem in this implementation so let's let this load and this takes once again super long the first time you load your page in nexts there we go okay so check this out we're going to go to the custom price and uh pay attention to the network tab right here okay so I'm going to drag this around bam and you can see well we still fire a request for every time the slider is changed but it's just delayed by 400 milliseconds and that's exactly not what we want we only want to fire one request this still completely cooks or API it spams the back end to hell right why is that happening well because the um the reference of the function is generated new each time that this component renders essentially this is now a new function when the component rerenders it has to do with how react Works under the hood and basically the function Integrity changes and react doesn't know that hey no you shouldn't run this function again right and to get around this problem it's actually really easy let's say con underscore oops underscore debounced submit and this is simply going to be equal to use oops use call back and we're going to wrap the debounced submit in this on callback and then the second argument we're going to pass in here is an mty array this is the dependency array and that we're going to leave empty so only when this component first renders the function Integrity is going to be created or the reference to the function rather I think that makes more sense and then it never changes essentially complete circumventing the problem we have just faced with the debones that is created new every time this component rerenders that's exactly not what we want right so this um underscore debound submit we can now completely spam as much as we want and only after 400 milliseconds pause will the function actually be called once and this is the function we're going to use for example in our apply array filter right here so whenever you change the color then the new products are going to be fetched let's already see this working right let's restart our application ation let's now remove the white color and Bam the white color is gone bam the beige color is gone Bam Bam the colors are gone very very nice we know this works now that means we can now use it in all the places where we need to for example that's going to be right here in the sort options if you change the sort options then we want to refresh our data so basically we can now call this everywhere where when the user changes something we want to refresh the data on the page that's also going to be not here because that's already happening in the apply array filter so we don't need to implement it there same thing for the size filters is already being handled for us here perfect we don't need to worry about it but we do need to change it for example for the price filters so that's also refresh the data once you select the custom option and also of course this is going to be oh never mind that the previous one wasn't for the custom option so the previous one the debound subit we put up here into the inputs this was for the any price the under €20 and under €40 so when you click one of these the product should be refreshed exactly and then right down here this is for the custom option once you select it and this one in the actual slider is then for once you actually change the thumbs on the slider and drag them around all right let's save this and see if it works let's go into kind of full screen here restart the page and see so can we remove colors yes we can and that works and we are batching the request together using the debouncing how nice is that man very very cool same thing for the size for example this is size s let's filter those out bam they are gone size M and size l no size s anymore very very nice and now for the price under €20 does that work yes it does under €40 yes that works as well now the products are more expensive and for example we could select a custom range from 0o to uh let's do 25 so all the products are filtered out as we drag around the slider very very nice we are almost done the very last thing you'll notice is for example if we only allow products from 0 to5 well there are no products matching that criteria however it the UI doesn't show anything this doesn't look good we want some kind of empty state to handle the case where for example all the filters are unchecked there is no product matching the required search criteria right and and adding that is surprisingly easy to do this we're going to create a new file in our components and then products where everything related to a product lives and create a new file called Mt state. TSX and this component is going to be really really simple let's say const empty State oops State and this is going to be once again of course an arrow function that we export default at the bottom the empty State there we go okay from here we're going to return a div at thep top level with a class name of relative a call span oops call- span Das full so even if we put it inside of a grid the entire thing is going to like it's going to take up the entire space basically um right afterwards a height of 80 a background gray of 50 we're going to say withd of full a padding of 12 a flex Flex Das call an items Das Center and lastly a justify Dash Center as well now it's inside of this diff we're going to put an X Circle which is an icon we get from Lucid react and let's give this a class name of height 8 a width of eight and a text red of 500 to indicate that something went wrong you know now right below this let's put an H3 element with a class name of font Das semi bolt and also a text of XL and inside of here we're going to say no products found perfect and right below this let's create a P tag that's last element of this component this going to get a class name of um of text zinc 500 and lastly of text- smm for small and inside of this paragraph we're going to say we found no search oops search results for these filters period that's not a period That's a period very very nice let's format this file save this file and the last thing we need to do is now actually use the empty State wherever we um needo on the page and that's going to be right down here where we render out the products because one check we can do right here right is if we have the products if the data came back from the back end but it's empty then we know there are no products matching the criteria and we need to show the empty state so The Logical check we can slightly modify it to products and end the products. length is triple equal to zero so we have the data but there is no products there are no products in the data in that case we are going to render out the empty State we just created like this and then just like before else so colon if we have products then we are going to map over the products and else we don't have products and we don't need to show the empty state in that case we are going to show the loading state so by slightly modifying this Turner operator right here with one more logical check can save that and then once we make a selection that has no product inside of it like for example a price from 0 to 5 then it's actually going to show or empty State because no product matches the description no product is between Z and5 very very nice and now I just noticed a bug that I didn't notice in the earlier version of the project and so we are going to fix it right now live together so what is the bug basically I just noticed when we unselect every color right here I think this worked in the demo project but it doesn't work in this project right here because what's happening is if there is no color selected as the filter right basically we are sending over an empty array to our back end not adding any filters because the four each doesn't get triggered from an empty array and basically we don't have any color filter applied not even that the color should be empty as we stay right here with the filters right so it's just going to search for any color in that case so basically what we need to do to fix this let's do this live together is do a little check if the color dot oops if the color. length is larger than zero and we are in our route. TS by the way in our backend in that case we're going to go through each color and add the filter and we're going to say else if the color. length is triple equal to zero so if an empty array is passed then we can say filter. add raw and we're going to add this for the color key and as for the filter that we're going to add that's going to be color is equal to just um an empty string so empty quotation marks no color is going to match that therefore we should be able to see hopefully um that this works now again I I'm just winging this together with you but let's see if that works let's exclude all the products and very very nice it does perfect so again I didn't prepare for this but but I just noticed that so let's just fix it while we are here um very very nice and then we can do the same thing for the size so if the size do length is larger than zero we are going to apply all the filters for it and then else if the size. length is triple equal to zero in that case we're going to say filter. add raw and this time we want to add the filter for the size and then the filter we're going to add is size is equal to an empty quotation mark just like this and we can leave the price as it is that works perfect let's save this and do a final check if everything works let's red out the page and let's exclude white and let's exclude green and let's exclude purple we don't want those colors in here and you know what just for demonstration let's only have beige products in here as well okay so size let's filter out the small ones and let's filter out the large ones so we only have medium products yes that works very nice and now if we select a price under 20 we should be able to see no products let's try this no products pH perfect under 40 we should be able to see two because there are two shirts under 40 and then um if we do any price we should be able to see the more expensive one as well perfect let's go for a custom price let's choose this everything should be shown let's choose until 40 only two should be shown and if we go this low then no products should be shown Perfect Dude that is so cool let's reselect all the filters that's going to show any product let's go for any price as well collapse the filters and I'm really happy with how this turned out if we now sort uh low to high we can see the cheapest products first as we scroll down it gets more expensive and if we select it the other way around then we're going to see the most expensive products first and then it's going to go descending to the cheaper um products super nice okay so we made sure everything works very very nice work that's the project and I really hope you learned a lot so great job following along I'm really happy with how this turned out and I hope you are too and uh congratulations man on finishing the project hey man very very nice job following along and I mean that like making these videos is hard like it was 4 hours of raw content I believe that I cut down then and you followed along with the whole thing like massive respect man it's like 2 and a half hours now maybe a bit more that's not easy and I really hope that you agree I kept my promise to go through everything step by step together and provide you with all the resources everything for completely free and I really hope you enjoyed this video like watching this video as much as I enjoyed making it so big respect for following along I really hope you learned a lot that you're going to be able to use in your future projects congrats on finishing the project very very nice work and that's it for me for this video again I really hope you enjoyed I'm going to see you in the next video Until then have a good one and bye-bye
Info
Channel: Josh tried upstash
Views: 19,326
Rating: undefined out of 5
Keywords: filter system, product filters, filtering, ecommerce, e-commerce, react, nextjs, web development, beginner, project, beginner project, ecommerce filtering system, josh tried coding, joshtriedcoding
Id: _017xTgnqGw
Channel Id: undefined
Length: 175min 8sec (10508 seconds)
Published: Wed Mar 27 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.