Build a Complete Digital Marketplace with Next.js 14, React, tRPC, Tailwind | Full Course 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey I'm Josh I work as a web developer here in Germany and in this single video we're going to build an entire digital Marketplace from beginning to end step by step together there's three things you're going to learn in this video first off how do you write clean maintainable code and that involves how do you write super nice reusable react components for example this component on the homepage and this on the products page they're the same super nice reusable component the product real and that's just one example of super nice reusability that you're going to learn with me in this single video second thing you're going to learn in this video is the design with all batteries all the professional artwork needed for a professional website is all included in this video and we're going to make it look amazing both on desktop but also of course fully responsive on mobile devices and third super important how do you write good software how do you architect good software what is the thinking behind the processes that is what you're going to learn in this video I don't want you to just call along but we're going to look at the fun mentals the concepts behind what we're doing to understand why we're doing what we're doing it's going to be super fun let's take a look at the project that we'll be building and here it is this is digital hippo your Marketplace for high quality digital assets there's a bunch of categories we can go through like the brand new section with all the newest products and the option to shop the entire collection all the products in that category right away we have the UI kits we have the icons all with beautiful products listed inside of them and some Airbnb style animations whenever we load this page things that are small but you expect to work and look good just work and look good in this application for example with these Airbnb style animations and also these Airbnb style animations we have beautiful Airbnb style image slider animations for each product as well that not only in the product detail page allow us to preview the product but also wherever we browse product so we can get a better idea of what a product will actually contain let's let's for example say we want to purchase this lemon milk icon set right here that looks good for $49.97 well if we currently look in our cart it's empty and all this stuff not only works but as I said I want to make sure this looks good all the professional artwork in this app is completely included in this project we're going to set up everything together so the final product will look hella good we can add this product to our card we can browse similar icons right down here for example these three 100 line icons these Universal icons anything you can imagine and it just looks really really nice let's take a look at our card here it is and let's click continue to check out well we can't because that is a protected page we need to be logged in in order to do that we also have the option to continue as a seller so not only can we buy on this platform but we can also list or own products which are then verified by admins so only quality products are sold on this Marketplace and anyways let's create an account together let's enter demo Josh Tri coding.com and we're also going to say anything one 12 three as the password and then hit sign up beautiful check your email we've sent a verification link to demo atj trite coding.com now since I don't want to show my entire inbox here here is the email that we have been sent to our inbox it looks really nice and let's hit verify account right here and you're all said thank you for verifying your email and we now have the option to sign in beautiful let's do that let's sign in with the account that we just created anything one two three as the password let's hit enter and we are signed into our account which means that now we can actually go to the card page see a summary of all the products we have in the card and have the option to check out on the right hand side right here now what happens if we for example have an empty card of course we also thought about that it's all included in this project all the stuff that you just expect to work just works but anyways let's go back ahead and add back that product into our card add to card there we go we really want this icon set right here so let's continue to check out hit checkout and that's going to take us to a secure checkout page hosted with stripe I'm going to enter my email demo jri coding.com let's enter the credit card details right here and right here and I'm going to say Josh s the card holder name and then that's it pay that's going to process the payment securely in the background and communicate with our server securely so that we can then provide the asset download links on the page we are going to get forwarded to here in a second which is going to be the thank you page so the payment was successful and we are forwarded to the order successful thank you page we've sent your receipt and Order details to our email by the way this is not a static page we are actually checking if the payment was actually successful or not and only if it was successful we are displaying a secure download link right here all right let's go down here we can access the seller dashboard or log out and the seller dashboard is one of the biggest features I haven't even shown you yet so any user on this app can create their own products on our store so let's create a product together let's name it landing page UI kit the price is going to be $9.99 we can enter any product details but that's completely optional and let's select UI kits as the category now for the product files this is what users actually pay for let's insert the files we want the users to be able to securely download once they make the payment for example I'm going to select this mockup of the project that we're currently building the digital hippo landing page now the product State this is something that only admins can change users cannot change this and this will determine if the product is actually listed in the store or not that way we can make sure only high quality products end up on our store now let's upload some New Media right here for the product images for example the main cover that we want our product to show in the store let's also add a secondary image upload new Med media select a file and then select the checkout image right here when we hit save this is going to create the product and make it available for an admin to verify and listed in our store and this admin dashboard is built using the sponsor of this video payload a backend to build pretty much anything typescript first developer first for example all the orders are all listed here that you made as a customer on or digital hippo platform all right I've verified the product from an admin account and we should be yep here it is we should be able to see it in the store it shows up right here as the newest product of course as a landing page UI kit that as a customer in the store we're able to preview Airbnb Style with all the beautiful loading animations and now actually buy it as well and by the way one thing I forgot to mention I'm going to put it on the screen right now it's the beautiful Reed email that we get once we complete a purchase right to our inbox by the way following along with this video is totally free no credit card required and you can completely go at your own pace if you if you like what I'm doing all I ask is click the first link in the description that's going to take you to the GitHub repo of today's sponsor payload and just start the repository they're fully open source and who knows they might want to work with me in the future again that's all I ask thank you and let's actually get building let's get learning how do you architect all of this how do you write good code and let's actually start with a project so let's get started this is where it all begins on the desktop let's open up our command line to create a completely new next S14 project from scratch together let's navigate to the desktop at least that's where I like to keep my projects anywhere you want this project to live and let's type in npx create-- app and then at latest because we want the newest NEX S14 version for our project let's give it a name I'm going to say digital hippo but feel free to call it whatever you want this is your thing your project that's going to end up on your portfolio so feel free to give it any name you want and the hippo will be nice nice because there's a lot of images I'm going to provide to you that have you know the hippo on it so it might make sense to call it something similar okay yes we want typescript yes es Lin to ensure good code quality we want Tailwind CSS for styling The Source directory yes as well yes as well for the ab router and lastly no we do not want to configure anything about the import edas that can stay as it is perfect and after some waiting that installed all the dependencies so now we can just CD change directory into digit hippo from our command line and say code Dot and I can zoom in a bit so it's easier for you to see code dot which just means that we're going to open up this project right here in Visual Studio code perfect and here we are in a completely brand new Next S4 application let's verify that is in fact 14 and it is we can see right here 14.0 perfect this is going to be a really really nice project let's start it up and see if everything works by typing in yarn Dev that's going to start our development server which is really fast in NEX js14 by the way let's open up Chrome or any browser of your choice into a split screen view and let's open up Local Host 3000 to see if everything works just as expected now we expect to see the default next S14 page and there it is beautiful let's zoom out I'm quite zoomed in and you should see the same the standard next S14 m page perfect now we can navigate into the page. TSX that lives under or Source SLA directory and all this is is the standard um main page because it is in the source directory the page. TSX is always served as the main entry point to our app so if you don't specify anything else in the URL this is the page that will show up we can get rid of all the content that is here already and I've got a super handy shortcut for this um you can see where if I select a HTML thing for example this diff and then press the shortcut it selects everything that is inside of that HTML tag so let me quickly show you how to do this if I can do it on the fly it should be under file then preferences and let's go to I think it's keyboard shortcuts and then it should be emit balance outward there we go this one right here this is my key binding it's crl m set it to whatever you want it's really really handy and it just makes working on this app and any app in your future much more enjoyable so we can literally just Mark everything get rid of it and start on the main page so for example let's say hello world and we can get rid of all unused Imports by hitting shift alt and O it's going to clean up and sort our Imports and get rid of everything we don't need and there we are we can see the hello worldle okay I did a typo anyways we now know that everything works just as expected which is perfect because now we can get started on the main build and the first thing we're going to do is the landing page we want to make sure this app looks really good so first thing we're going to do is get set up with our UI library and I might zoom in a bit more so you can see it easier um but if the code gets a bit longer then I might need to zoom out again okay anyways let's get set up with our UI library to make sure this app looks really good out of the box we are going to use a UI Library called shat CN for that let's navigate over to ui. chat cn.com that is this UI library right here and it's zoom out it head it looks really good it's fully customizable with Tailwind CSS and it doesn't abstract anything away from you chances are if you've ever seen any video of this channel you already know it it's very very nice and this is the library we're going to use in our project as well now to get started let's type in npx shat cn- UI at latest and then in it that is going to create a components. Json file right here on our left hand side and ask us a couple of questions for example if you want to use typescript hell yeah let's go with the default style in slate as the base color or global. css is in the source directory so we need to change this to source and then global. CSS and the CSS variables we're going to say yes you're going to see what that looks like here in a second and our Tailwind doc config is a typescript file so let's change this to Tailwind doc config.sys on the left hand side that is what it's asking for and the add components is totally cool we can just leave that as it is and the ad Li tools is also fine you're going to see what that does here in a second it's basically just a custom import path that we can then later use yes we are using server components and we do want to write this to the component adjacent so let's hit y for yes and that's going to create the component adjacent file I mentioned earlier right here where it just saves a bunch of UI Library internal you know things that that we don't need to worry about we're never going to touch this file actually but what this does allow us to do is to get the styling right out of the box for example if we navigate into our Tailwind doc config.py this component library and essentially what this allows us to do is make sure that our app looks really good in any page not just the homepage with only a couple of changes so for example let's navigate into the layout. TSX that lives in our source slapp the main layout where everything is rendered for example the entire app will be rendered as the children depending on which page we're on right so this is a wrapper for literally the entire app which is really useful for defining some custom styles that we want to apply everywhere basically for example on the HTML let's apply a class name of height fo and then on the body let's also give it a a class oh it already has a class name never mind we can just go ahead and change this class name then and this class name will be a joint class name where we want to give a class name for a font and also some default Styles so what does that mean first off let's give it a relative style a height of four a font Das sence and lastly NTI alest there we go and if we hover over this we can see what CSS is actually being app light and that is because I'm using an extension you can see that right here that is called Tailwind intelligence there we go this is it if you don't have that make sure to install it that allows us to hover over these classes and see what they actually do in regular CSS it's very very useful and we can also give the entire app a custom font and that font is already pre-installed right here and that is the inter font that we get from next font Google however we still need to apply that and that is what I meant by a joint class name let's cut out this class name we just wrote using control and X we're going to need it here in a second but let's make this Dynamic and use a CN helper function in this CN helper function we can invoke this comes from or UI Library let's take a look at that in one second first off let's just paste in the class name we had previously and let me zoom out a bit I think this is a bit too much so it's just easier to see there we go and now we can actually apply multiple class names also dynamically for example we can insert a comma at the end here and then apply the inter. class name to or main body that means every every content every page in our app will have the inter class name applied to it and therefore use this font let's save that and take a look at this CN helper function it's just one line of code essentially what it does very very simple it allows us to use conditional class nams you're going to see that a lot throughout this app this is an insanely useful function um we're going to make use of it in a lot of different places and essentially um it just allows us to you know do things like this where we can join classs together then also later on apply them conditionally perfect let's save the layout okay and now for the body part let's not just render out the children but let's also wrap this in a main tag that we can give some class name for example let's give it a relative a flex a flex-all um to align this vertically So Below each other the content inside of it and a minimum height of screen and what that's going to do is make sure that our page never looks weird and never um you know takes up only like half the page but always at least takes up the full page that's going to be especially useful later when we add a footer so we make sure it always stays at the bottom and not when the page doesn't take up the whole Space right here then the footer moves up that's not what we want that looks horrible so let's save this layout that's essentially what we just did by adding this right here and let's also wrap our children in a div it's going to be one of the last changes we're going to do in this layout and this div is going to get a class name of flex D grow and a flex of one to make sure the children fill up as much content as they possibly can so when we later add the footer right here let's not do that right now but when we later will add that then we can make sure the footer is always put to the very bottom of the page awesome let's save that and restart our Dev server so we can actually see what the hell we're even doing let's put this into a split screen there we go or Dev server should be starting and let's see can still see the hello world perfect okay let's navigate over to our page. TSX and actually get started in you know making this look good in writing the hero section for example um for or app and the first thing we're going to do for that is create a new new component so let's go in our source directory and we have a directory here called components I'm not sure where that's from cuz we didn't just create that um anyways make sure let's let's just delete that let's create a new folder in our source called components there we go this is where all our you know react components or serers side components are going to live and inside of here let's create a new file called Max with wrapper. TSX and this this is going to be one component we're going to write that makes sure that the entire site looks good in terms of layout let me show you what that means let's get started writing and let's say const Max with wrapper is going to be equal to an arrow function and let's give this a bit more space because we don't really need the page right now and this Max with wrapper is going to take two properties we can destructure them right away like this in the arrow function and these properties are going to be the class name that we might want to apply and also the children so the page content as you're going to see in a second and because we are in typescript we also need to define the type this will have for example let's give this an inline object type and the class name will be an optional that's why the question mark here because it's optional we don't need to pass it a string and the children are going to be of type react node it's a type that react provides to us we don't really need to worry about it that we can simply import at the very top the react node type from react just like that and typescript is happy with us perfect and this component is going to be really simple let's return something from here and that is going to be a div this div gets a class name of mx- Auto oops Auto there we go a width of four a maximum width of screen- XL so if we hover over this this is going to be a 1,280 pixel limit that we can add Maximum take up and lastly we're going to apply a padding X of 2.5 and that's going to be 2.5 and on medium devices end up a padding X of 20 there we go and let me zoom out so we can uh just see this perfect now inside of this div the only thing that we're going to do is render out the children and one thing do you remember what I told you earlier about the class names right here we are hardcoding a class name but what we're going to do here in a second is use this Max with wrapper by the way we still need to export that really quick export default Max with wrapper so we can use it in other components at the very bottom of the file and if we now want to use that in the page. TSX for example because this is a component that we're going to reuse a lot across our app to make every page look the same in terms of layout let's use it right here the max with wrapper and what might happen is that we want to change the style slightly for one page that we're on and that's what this class name right here is for however currently we're not even making use of that class name and that's why the CN helper function is so useful let's grab the class name insert a curly brace right here for a conditional or a dynamic class name and use our CN helper function in here we can pass the previous Styles as the default so these will always be applied for this class name however we can then join it together with a class name that we can pass into this component as a prop so these if we pass them in will override the defaults however we don't have to pass them and then all the defaults will apply so we could now pass a class name right here but we don't have to so it's a very nice reusable component that we can reuse across or entire app that is why this Max with wrapper is so nice Okay inside of here let's create a div and inside of this div for the max with rapper let's give it a tad more space and zoom in let's give this div a a class name oops not an on click Handler a class name there we go of padding y20 an MX of Auto a text- center a flex a flex-all to align items vertically instead of horizontally and I really might need to think about how to stay at one level for the zoom CU I keep zooming in and out I hope this is not too small I hope this works let's give it an items stash Center and a maximum width of 3 XL for this div right here perfect we can already change this and take a look at our page currently we can't see anything on the page and one thing I'm wondering is why our entire Page by the way is black right now so let oh okay and that might be the reason we have two global. CSS um so let's see which one is the right one let's give this a bit more space for now and we definitely don't want two global. CSS so this one right here in our source directory is the problem because it's sets the wrong color so let's delete the global. CSS in our or in our app directory there we go under Source app delete that global. CSS this one right here let's get rid of it click delete and these global. CSS where are they by the way in just our source directory let's move them into our app so essentially we are replacing the old one let's restart our Dev server and see what happens and hopefully there we go our page is white now that looks much more um like we want it and by the way this all comes from our UI Library if you're wondering why this is generated and these Styles look good out of the box we can easily replace them if we go to our UI Library website and then under themes by the way it's ui. chan.com and then under themes you can choose any uh any custom color theme that you want for example blue or green hits copy code right here and this code that you can copy right here is exactly what we have right here on the left left hand site in our global. CSS and we will actually make use of this feature um in actually we can just do it right now why not do it right now let's go into a split screen view click under themes and then let's actually go for a what kind of them do we want I used the blue one earlier and I think I'm going to go with a blue one but choose any color that you want right here that you like you can see the previews on this page and then hit copy code and let's just copy all this over into our app and that's going to change for example default button Styles and whatnot to that color so our buttons are primarily going to be blue let's replace all these variables inside of our global. CSS hit save and while we don't have any text on our website yet that's going to take effect here in a second let's navigate back over to our main page. TSX right here under Source app and then page. TSX and actually get started in the landing page so for example in the div that we just created right here let's insert an H1 tag a main heading saying your Marketplace for high quality high- quality inside of here let's put a span element with a class name of text- blue- 600 and inside of this span element let's say digital assets so that's going to be um highlighted in color let's give this a bit more space there we go and our development server is running so why don't we see those changes here there we go your Marketplace for high quality digital assets doesn't look perfect yet we're going to fix that here in a second and let's put a dot after this span okay now for the H1 cuz this really doesn't look that good let's let's give it some class names to make it look good and by the way we can hit right click on the page. TSX and hit close others just to clean up the workspace a bit you know that's what I like to do and so we have only one open file okay for the class name for the H1 let's say text 4XL a font Das bolt a tracking Das tight and if we hover over this let's see what it does it's the letter spacing so everything is a bit more together it looks a bit cleaner and then as for the color let's give it a text- gray- 900 very dark and on small devices and up let's give it a text of 6 XL to make it like really large let's save that and let's give it a bit more space there we go your Marketplace for high quality digital assets and it's only so large because I'm so Zoomed In by the way if I'm at a normal zoom level it will it will look much better than you know the 200% I was at very very nice that's the first building block of our website really good that's the H1 element right here let's expand on it for example let's put a P tag right below it saying welcome to and then now insert your name for this app I'm going to say digital hippo cuz that's what my app is called if you have anything else related to the hippo because the you know all the image that I'm going to provide you are related to hippos but put anything you want again the images are related to hippos that I'm going to provide you all the artwork contain some kind of Hippo so it might make sense you know to include hippo and but name it whatever you want right and then welcome to digital hippo period every asset on our platform is verified by our team and let's actually give this a line break to ensure or highest quality standards there we go of course you don't have to copy this text as it is type anything in you want I just feel like that's a pretty solid um you know introductory text in our hero section let's give this pag a class name to give it some styling this is going to be a Mt of six a margin top of six a text- large a maximum width of Pros which is I believe yeah 65 CH it's a very nice width for text that we can always use on P tags and let's also give it a text of muted Das foreground which is a color that our UI Library provides to us that we can simply use right here for this ptag and our page is still running perfect let's see what this looks like and if I wasn't zoomed in so much it would look a bit better there we go there is our ptag perfect let's have some call to actions right below that let's put them in a div and this div will get a class name of flex a flex d call to align these items vertically and then on small devices and up a flex Das row so only on mobile will they be below one another the buttons that we're going to create and then on large devices and update are going to be left and right that's what we're doing right here with a flex call and flex row on medium or on small devices and up because Tailwind is mobile first so small means from small devices and larger that's a Tailwind thing a gap of four let's give it and lastly a margin top of six as well inside of this diff let's put a link tag right here that comes from nextlink and this link is going to get an hre off and then slash products and the link is going to say browse trending so we can browse trending products when clicking this link and this link also needs to get a class name because if I just save that we can see wow this looks incredibly bad like there's just some plain text there and we're going to make use of something really really cool to make make this link look amazing and that will actually also come from our UI Library so let's say npx shed cn- UI or UI library at latest and then add and let's add a button right there let's hit enter that's going to maybe ask us a couple of questions no it won't CU we already answered all those questions at the beginning at the init stage perfect so it's literally just going to add this button component by the way if you're wondering where it does that it's under components UI and then right here this is what we just did by typing in that command in our CMD it's a super usable super well-looking um styled react button component essentially that we can already now use in our app for example to make this link look good let's make use of the styles that are applied to this button and these Styles come from these button variants right here if you've never used this syntax essentially it applies some default Styles at the very and then depending on the button variant that we can now choose um it's going to apply some additional class names so let me just show you how this works right it's going to be a bit easier for the class name let's enter button variance and simply invoke that and just by saying that all the default button styles are applied for example if we now restarted our Dev server let's start it back up and restart the page once it's done there we go that's one really nice feature about next S14 by the way super fast development experience much faster than in nextjs 13 there we go or link is now styled in the primary color that remember we have selected earlier from this teams page right here if you chose green or orange then your button would now be green or orange so this is a really nice way to ensure that your app just looks amazing okay and then right below the link element still in the div let's create a button component and this button is going to say or quality promise there we go and then a little arrow that we can simply insert by typing and r a r r and then a colon It's like a builtin thing we can use as a little right arrow right here if we save that however we don't want this button to be blue and these are the variants that I just talked about now what do these do we can simply give our button a variant of for example ghost there are a bunch of variants we can choose from but let's go with the ghost one so essentially it's a very subtle very nice button um in our call to action section right here on the website how amazing is that we just got an amazing looking hero section in like 20 minutes or 10 minutes or however long that took that is super super cool and that's going to make sure um we can reuse everything across our entire application have a unified style across the app and it all looks just really clean and is also accessible for example when pressing tab or hitting ENT um all the accessibility stuff is there as well in this UI Library perfect now below these buttons and the hero section that we have right here we later want to display all the products in our store right now we don't have any products so for now below the closing div above the max with wrapper let's just insert a comment that says too um you know list products for example so that we can later go ahead and search by to-dos and then um you know so so we don't forget anything in the entire application perfect and after this Max with rapper so below it let's create a new section and one thing will happen very fast and that is that you know react complaints hey you can only have one top level child in this component so the way we get around that is by just creating a fragment an empty thing that will literally not add anything to our Dom so that's great and we can still have you know uh two children at the same level inside of those fragments um Okay so perect let's get started in this section right here and give it a class name of Border DT a border gray of 200 and a background gray of 50 to kind of separate it from the rest of the page awesome inside of this section let's open that up in here goes another Max with wrapper and this time it will get a class name of padding y20 so that is why it's so nice to be able to apply a custom class name to this Max with rapper and because we can literally change it anywhere we want and all the defaults by the way are of course still going to be applied these will still apply and now we are adding dynamically this padding y of 20 from wherever we are calling the max with wrapper very very nice inside of here let's create a div with a class name and this is going to be grit grit calls D1 for one column a gap oops a gap y of 12 on small devices and up we want a grid called of two a small on small devices and up we want a gap X of six on large devices and up we want a grit ds-3 let's give this a bit more space so you can see better on large devices and up we want a gap X of8 and on large devices and up we want a gap y of zero let's quickly go into full screen so if you in case you missed anything this is the entire class name pretty long one I know but it's it's uh definitely one of the longest ones in this component so don't worry and inside of here we're going to map over some perks so this is going to be like the feature section what differentiates us from the competition you know what makes digital hippos so special and in order to do that we can just go ahead to the very top of our file and declare a constant for this because this doesn't really change we can just decare a constant called perks for example and this is going to be an array of objects where each object in here takes a name of that perk for example in oops instant delivery that's what makes digital hippo special you instantly get your files after you purchase them no waiting time and now for the icon you want every perk to have an icon associated with it and as the icon Library we're going to use Lucid dodev or Lucid react and in here there's a bunch of really goodlooking icons as svgs that we can simply use as is in our app so let's install it as a package really quick let's say yarn ad and then lucd D react that is this icon library and by the way I encourage you to follow along using yarn or npm I typically also use pnpm for these projects but as you're later going to see self-hosting with an Express server is not always that easy with pnpm so for this project it makes much more sense to use either yarn or npm and don't really uh worry about pnpm because there was one bug maybe it's fixed by now I'm not sure but it's just easier and you won't face any issues with npm or yarn so that's probably the way to go for this video and that's why I'm using yarn perfect okay now inside of this perks component right here let's give it an icon or not component just an array of objects let's use the arrow oops arrow down to line icon we can now simply import right here from this um icon Library if we type this name in right here by the way we can just filter by it this is going to be the icon let me zoom in so you can see this easier and that's going to show up here in a second perfect and as the description for this let's say get your assets delivered to your email in seconds or minutes uh let's let's leave it at seconds sounds a bit better and download them right away period that's like a big Park of digital hippo you know you get your stuff right away perfect we can just cop this down honestly and uh save ourselves from declaring everything again let's just change up the name and by the way if you're wondering how I did that I marked everything and then hit shift alt and arrow down that's how you can and just copy stuff down let's say guaranteed quality right here for the second one as the icon we're going to use check circle from um Lucid react as well and for some reason the Auto Import doesn't work so let's just take it and import it right here that's n works perfect and as the description let's change this up as well let's say every asset on our platform is verified by our team to ensure our highest quality standards of course this is just something I made up you know of course you can input your very own text right here this is just kind of a you know template I'm giving you that you can use but of course you don't have to let's say not happy question mark we offer a third DH day uh refund guarantee period okay and last thing let's copy this down one more time because we're going to have three perks and the last one is going to be for oops for the planet and this is going to get an icon of leaf to kind of IND indicate a you know uh let me show you what this is like a natural feel I guess let's also import that leaf icon from lucd react right up here that means we can now use it as the icon and as for the description let's just say we've oops and we need to put this in double quotes actually so we can say we and then you know the single quote we've um pledged 1% of sales to the preservation there we go and restoration of the natural environment period and again this is just something I met up feel very free to um put anything else you want that would you know make your version of digital hippo your shop right here that we're building your digital asset Marketplace um stand out it doesn't have to be um what I put there awesome let's give this a tad more space so we can work on The Styling right here so let's go into this div Rec created right here with a very long class name and inside of here let's map over these perks so perks. map so for each one we can display a separate element let's receive each perk as the first parameter or argument I keep mixing those up anyways let's uh have an arrow function in here and let's receive it as I think it's the parameter there we go yeah argument is what we pass in parameter is what we receive so it's a parameter anyways it doesn't matter for every Park let's map over them let's create a div element and by the way let's not use these um curly braces but these round normal braces so we're directly returning some jsx and because we're mapping we also need to give this a key it's going to be a park. name um okay perfect this div is going to get a class name of text- Center on medium devices and up it's going to be Flex on medium devices and up a items D start medium text- left large and we're going to apply a block on large devices and up just like that and lastly on large devices and up a text Dash Center let's go to full screen just in case you missed anything that's the entire class name and inside of this div right here let's create another div this one is going to get a class name of medium flex-shrink Das zero a flex and a justify Dash Center there we go let's open up this div and inside of here one more div and this one gets a class name of height 16 a width of 16 a flex an items D Center a justify D Center a rounded D4 a background blue of 100 so a very light blue background and a text- blue of 900 so a very dark text so it's a very nice contrast as you're going to see here in a second this is the entire class name in case you missed anything let's open up this div and inside of this div let's just display the perk. icon just like that and we need to call this as a jsx component we can't just insert it like this so let's put it into these um HTML kind of angled brackets or whatever they're called you know those triangles and give it a class name of width one3 and a height of 1/3 let's save that see what happens let's restart back up our Dev server so we can Che so we can see the changes right here on the right hand side let's close out of the development console restart our page and ideally we should be able to see beautiful these three perk icons showing up on Earth page that looks awesome now let's also display the text we have for each one of them right and we didn't just write that for no reason so let's go down with two closing divs and one closing div to go open that up so we have one closing div and then all the brackets kind of right here and let's create a new div right in here this is going to get a class name of margin top six to kind of distance it from the icon on medium devices and up a margin left of four on medium devices and up a margin top of zero large devices a margin left of zero and large devices a margin top of six there we go inside of this div let's create an H3 tag that's going to contain the park. name so the main kind of title this H3 is going to get a class name of text- Base a font D medium and a text Gray of 900 perfect let's apply some formatting and inside of here also goes A P tag right below the H3 that's going to get a class name of margin top 3 a text- small and a font Dash muted oops font a text not font muted foreground so that's for the color there we go so margin top three text small text muted foreground and inside of this ptag we're going to display the park. description perfect that's hit save and wow that looks really good right away let's go into full screen perfect fully responsive on mobile Mobile on desktop it looks absolutely great all right very very good job that is a very important piece of our landing page done right there the perks the hero section and now comes a really important part and that is going to be the nav bar right no page is complete without a nav bar it's especially important in our e-commerce app so people can you know go to their account and log in and log out and browse the different categories like icons and fonts and you I kits and whatnot all through the Navar that's why it's so important to our app and let's go ahead and create the Navar um to style it you know let's go into or by the way you can close this button right here at the very top that's just going to close all files and kind of clean up the work workspace let's close out of those as well and now let's go into our components and create a new file called navb bar. TSX let's say cons navbar is going to be equal to just an error function just like this and let's return you know just nothing for now let's leave it as an empty parenthesis and Export default Navar so we can then use it across our application awesome inside of this Navar let's create a div component and this top level div is going to get a class name of background white a sticky property so the nav bar is always visible even as we scroll a z index of 50 cuz we want to be you know on top of everything else a top of zero to make it stick to the top of the page you know where Navar should be and not the left side right side bottom whatever an inset X of zero so it spans completely from left to right and a height of 16 perfect inside of this div let's create a header oops not a head a header element just like this and this will get a class name of relative and a background of white as well inside of this header we can make use of our trusted Max with rep to make sure we get the padding and everything just right also on the Navar so it looks very good across the entire app let's apply some formatting really quick and inside of this Max with rapper let's create one more div with a class name of Border DB and a border gray of 200 so a very subtle color it's going to look really nice and inside of here one more div with a class name of flex a height of 16 and items Das Center by the way this is where our mobile menu will live in a second so let's create a quick to-do comment um so we don't forget it um mobile nav this is where it's going to live if we are on mobile devices and we're going to do a different style for the desktop and large devices of course um so we can make sure it looks good on all devices so at the same level right here as where the mobile nav will live in a second let's create one more div and this will get a class name of margin Left 4 A flex and a on large devices a margin left of zero perfect inside of this div is going to live or logo and one very important accessibility thing we want is when you click on the logo or not really accessibility it's more like user experience when you click on the logo let's create a link component in here we want to be taken to the homepage so this link is going to get an H of just a slash which is going to be our homepage and inside of here is going to live our Lo logo and our logo is nothing else than an SVG file and we don't really have the logo in our app yet so let's create a new component for this logo but let's call it icons. TSX so in case you ever have any more icons as SVG spit you want use in the app we can always put them in this icons file right here inside of this icons component let's export a cons icons this is going to be a very very straightforward component it's simply going to be an object not even a regular you know react component as the logo let's receive some props first this is going to be an arrow function that returns some jsx at the end of it but first let's receive some props and these props that we can receive in the logo are going to be of type Lucid props it's a type that our icon Library provides to us and by the way if you ever get that you can't Auto Import things you can always press shift control NP and reload the developer window and in 99% of cases that's actually going to fix or Auto Imports so let's let that load and now the Auto Import is suddenly there magic and now it just works so we get it from bed react as well perfect and inside of this logo let's return an SVG and this SVG is going to be in a copypaste list that I'm going to provide to you so there's no point in literally typing out the entire SVG that's completely pointless and so just to save some time and make your life easier I'm going to put a a copy paste list in the description with a link that you can just go ahead go into it's going to be in the same GitHub repository as this entire project grab this SVG and simply paste it right here as the logo and everything will work out of the box one thing we do is still spread in the props right here into that SVG file so I'm going to give you some time right now go ahead grab that SVG from the GitHub repo paste it in it should look just like this so for the jsx that this logo icon returns and let's spread the props into this SVG file so that we can style it from wherever we want to call it for example or navbar right let call the icons let's import that. logo which now works and these props allow us to give it something like a class name for example with a height of 10 and a width of 10 so wherever we call this we can still specify the size of it for example let's save the Navar and right now nothing really will happen and that is because we're not using the Navar anywhere so let's navigate over into our main layout. TSX because this is where we're going to insert the nav bar and this is going to live right above or div with the children so let's put it in here a nav oops a Navar there we go from add component slnv bar let's it save and there it is our Nava is right there and as we scroll it will stick to the top beautiful right below this div containing our logo will live the very important part the nav items so the UI kits and icons and whatever that we we that we will be able to open up by clicking on them so let's create one more div in here with a class name of hidden a z index of 50 as well on large devices and up a margin left of eight on large devices and up a property of block and lastly on large devices up a self- stretch let's give this just a bit more space there we go and inside of this div let's insert a component called nav items this component doesn't exist yet we're going to create it together here right now and this is going to handle all the logic for the nav items that they're going to show up in the nav bar okay and essentially this is only for the desktop so that's why we're hiding it by default and then only showing it from large devices and up so this nav items doesn't exist yet let's go into our components folder and create a new file called nav items. TSX let's say con nav items is going to be just a regular Arrow function that we can then export oops export default nav items R load and simply return um you know kind of nothing for now I guess and there's some really important logic we want to handle in the nav items and that is actually going to demand some client side interactivity and in nextra S14 by default this is a server component that means we can't have interactivity and to change that let's insert the use client at the very top of the file which essentially turns this into a client side component where we can now make use of of things like State and whatnot that we can't use in a server component actually let me show you just instead of explaining what happens if we left this out first off let's import this component in or nav bar so we can get rid of the arrow right here go into the nav bar import it and then inside of the nav items the first thing that we're going to do is return a div from oops a div not a Divide from this component there we go this div is going to get a class name of flex a gap of four and a height of full perfect now if we tried to do something like this where we have a state called active index and a set active index by the way keep following along this will be important for later but it's also a really nice um example to show you this is going to be a use state that we get from react and again for some reason let's reload our window using control shift and P cuz the Alo Imports are not working for the use State there we go now it works and by the way if you're in typescript you can pass this a generic these angle brackets that's going to contain the type the state will take for example a null or number that's what's or state going to be by default it's going to be null right here if we try this and saved everything that is going to throw an error you're importing a component that needs use state that only works in a client component blah blah blah doesn't matter essentially that's why this use client at the very top is so important that actually makes us able to use things like state for example like this just the basic react stuff um for example and then the server components are really good for data fetching that's what those are for okay essentially what we want to do in this component is map over a bunch of categories like the UI kits and the icons and whatnot and for these categories it makes a lot of sense to declare them in one separate place because they're going to be important throughout the literally entire application that's why it's really smart it's closed out of all of this clean up the workspace to put them in a new folder for example called config where we keep all important config objects that are important for the entire application where it doesn't make sense to give them locally in one component let's create an index.ts inside of this config file now as for the config that we want to Define let's say export con and then let's call this product in all caps by the way because this is a constant that doesn't change underscore categories there we go and again because this doesn't change it's a convention to put it in all caps to know um that this is an actual constant and not a value that changes at runtime inside of here let's create an array and this is going to take the Navar categories that we want to have for example let's give this a label and this label is going to be UI kits let's also give it a value and this value will be like an internal ID that we can use and this needs to be you know we can't just make it readable let's say UI uncore kits so we can actually use it as the value because for example if we wanted to put this into the URL like later we can't just use something with a space so it needs to be serializable just like this value and let's also say as const and this is going to be important for typescript later so this is not any string which it would be by default but typescript knows it's literally a string that is UI undor kits that's why we have this as const right here and as for the Fe stuff in here that we want to show in the Navar later let's say for example an object that has a name of editor pic so whatever or editors think is cool as the UI kits for the h ref we're going to give this let's just put a hashtag for now doesn't lead anywhere we're going to change that later of course and as for the image source this is going to live under slnv SL ui- kits SL mixed. jpack which is an image we are are assuming right now by the um relative path right here will live under our public folder slnv SL whatever whatever and of course that doesn't exist in our public folder by the way we can get rid of everything that's in our public folder right here and you can very easily get these images in the GitHub repository for this project now it might not be this exact repository right here but if we go ahead and view code and into the public folder right here um it should be right here in the nav folder just go ahead download this folder because this contains the static images we're going to use for the navbar for example the UI kits and the icons that we're going to use right here um in our Navar so go ahead download that folder and drag it right here into the public and that's going to make these images actually resolve correctly of course if you prefer you can also make them Dynamic later on that's not a problem and for now it is much easier to follow along though so I'm going to do the same just drag the nav folder in here and now we've got the icons and the UI kits what we're going to use in the nav bar again if you later want to get these from actual products in your website instead of making them static from the nav repository not a problem for now it is easier though so let's copy this editor's P down and let's give it a different name for example what we also want is the new arals arrivals section right here the image source is going to be a bit different instead of the mix let's make it the blue. JPEG and let's copy this down one last time and this time we're going to change it to Best Sellers and the blue right here let's change it to purple. jpeg perfect and let's copy down this entire UI case object for a completely separate category and this category is going to be the icons as the label in you know uppercase ey and as small case icons as the value that we can you know put into the URL again for example let's change the editor pick to for example favorite icon picks let's change the image to instead of UI kits of course this is all going to come from icons and if you wonder how I just did that marked one and then selected all the others press contrl and D to separately select all of them and change it to icons just like this and for the image let's have px. jpack as the first one new. jpack as the second image source and best sellers in smaller case be. jpeg as the last image there we go okay and we can for example change this to bestselling icons just to kind of customize it perfect and that's our config actually done we can save that we're going to think about these links later on that they navigate to the correct page that's going to be a really cool part actually um by the way anyways for now let's leave the config as it is cuz that's already going to work um for our use case where we can make use of the config right here in the nav items by mapping over the config so in here let's open up a curly brace and say productor categories or Conant that we just created map and for each category and also we want access to the index as the second parameter let's return some not jsx directly so in curly braces and then later on only return because we're going to do some calculations Right Above This for example we want there to be a handle open constant con handle open this is going to be a arrow function and inside of this handle open we're going to say if there is an active index that is triple equal to the index that we are mapping over right here in that case we're going to set the active index to null and else we're going to set the active index to the index that we're currently mapping of so this is how we keep track of which item in the Navar is currently opened and which one is not that's what the handle open right here is for and then for the const is open that we are declaring where we can just see is this item currently open or not this is going to be equal to I the index that we're mapping over is triple equal to the active index and that's how we can change the styling based on whether this item is currently opened or not and as for the return statement we're going to return a nav item component which is also a custom component and this will contain each element separately that allows us to style it very nicely and this is a component that doesn't exist yet so let's quickly go ahead and create it under our components folder create a new file called nav item. TSX and naming very similar just the singular instead of the plural for the NV item the const NV item is also going to be an arrow function just like like this and return a div oops return a div at the very top level but also don't forget we still need to export this as the default export default NV item at the very bottom so we can then use it in the other components for example in the NV items or we can simply import this component now perfect for each nav item let's give this div a class name of flex the topmost div and create one more div inside of here with a class name of relative a and an items Das Center inside of here let's use our button component that comes from our trusted UI Library this is going to get a class name of Gap 1.5 and in this button we want to display the current category for example UI kits or icons right and we're going to do that through a prop that we need to receive in the nav item so let's receive a bunch of properties actually and let's just give them an interface called NV item props and this this interface we still need to Define that this is how we tell typescript what type the properties we receive in this component will have the interface nav item props that's defined right above the component is going to take a category and this category is going to be of type category this is a custom type that doesn't exist yet and there's a really neat trick we can use to generate this type right here let's say type category is going to be equal to and then the type of product underscore categories or constant however if we hover over this this would be literally the entire type and we want this to be one category currently it's an array and to get just one category we can insert a number in this angled array kind of uh brackets right here so that is how we tell typescript we want one element of that array and therefore if we look at the very end it's not an array anymore and we have the type of one exact category which is exactly what we want we also want to receive a handle open function the one we just declared in the other component this is going to return void so pretty much nothing that's what typescript language is the is open property of Boolean and lastly the is any open also of type Boolean if any current element is open in the NV item section in the nav bar and those are all the properties we need we can start destructuring them is any open category the order doesn't matter by the way just get everything and if you're wondering how I'm getting this intelligence right here it's uh control and space bar so because we told typescript what the type will be typescript now knows these are the properties we're going to receive in this component so we can now use them in our nav item component for example in the button let's put in the category do label just like this so this is going to be for example icons or UI elements it's also give this button an onclick Handler and this is going to be the handle open that we pass into this component by the way because we're going to need some client side interactivity for example the on click this also needs to be a client component this can't be rendered on the server if we want for example um event handlers like this because those are inherently linked to the Dom and the Dom just doesn't exist on the server as for the variant this button is going to be it depends on the open state so if is open is true in that case it's going to be secondary the variant else we're going to give it a ghost now we're going to see exactly what this does here in a second okay perfect and let's also put one little thing inside of this um button right here and that's going to be an icon a Chevron down icon we get from our icon Library bled react with a class name and this is going to be dynamic so in cardi braces this time as the default class names we're going to apply oh by the way this needs to be or CN helper function right here for dynamic class names we're going to say height of four a width of four a transition D all and a text- muted D foreground just like this perfect and we're going to join this together with a conditional class name so as an object right after the strings separated by a comma just like this we're going to say we're going to apply a minus rot oops minus rotate rotate minus 100 80 if so that's why it's conditional if the is open state is true and if it's not this class name will not be applied that's why the CN is so useful it allows us to conditionally apply this rotated state to indicate if the current category is opened or not however we're still not passing any of these properties from the parent we still need to do that so right now typescript is complaining hey where are these properties right you're not passing them for example let's say the category is going to be equal to the category that we're mapping over the handle open is going to be our handle open it's the same name very easy the is open we've already determined that as well we can simply pass it in as it is as for the key we still need to pass because we're mapping over something and the top level element inside of the map always needs a key to be identifiable is going to be the category dot value and as the is any open right here we can just say is any open there we go now the question is when do we know if any category is open we know when each category itself is open but it's actually very simple so we know that any of the options in the Navar is open so I user clicked on them for example if the active index is not null so if there's a number in here it means there is an active category so let's say const is any open is going to be equal to the active index being unlike null so any number number right in there perfect let's save that and see if it works let's reload our page and is the dev server running yes it is but it seems like yes there it is perfect we still haven't done the mobile menu That's why nothing shows up right here but if we are on desktop devices or anything larger we can see we can actually now click and open these categories it might be pretty small for you let me zoom in and if it's in the open State the style is different and there's a little indication for this Chevron that this category is opened all awesome really really nice so what's now missing is the actual content you know of the nav bar sure we can open the things but there's nothing showing up at the moment and you know there should be something showing up the content of the nav bar and that's going to happen in each nav item so we can close out of all the other tabs and let's just focus on this this nav item right here let's go below the closing button with one closing div and after that with with also one closing div left to go let's do a conditional check here and if the is open is true then we're going to render out some jsx and if it's not true then we're going to render out n so we're only going to show some jsx right here if this category is currently opened and the jsx that we are going to show is going to be a div that's going to get a class name that is going to be dynamic based on something you're going to see in a second so because this is going to be dynamic we're going to use our CN class name helper function as the default styles that are always going to be applied let's use absolute and in inser X of Z so that's just going to apply a left and right property of zero it's just basically a you know short hand for that a top of full a text of small and a text- muted Das foreground just like this now for the conditional part that you might know how to do by now it's by putting a comma and then opening up an object and we can conditionally apply a class name that is is going to be animate Das in a fade in of 10 a slide in oops in from dashtop das5 and we are only going to apply this class name if there is not anything open so if not is any open there we go so it's going to animate in very very nicely and no I'm not too worried about Windows security right now uh anyways right below this div so inside of it and then right here let's open up a div element and actually this is going to be self- closing it's just going to be a little you know backdrop we're only using this element to add a little Shadow and uh we're going to give it a class name of absolute and inset of zero so that's top left right and bottom basically a short hand for that a top of one/ half and a background white and lastly a shadow property right here and because this is only decorational we can only add we can also add an area- hidden off and then true so for example what this does is when you're on a screen reader so if you're for example visually impaired this element is not going to show up for you because it's not relevant if you can't see for example it's only for decorational purposes and would kind of confuse screen readers so we just don't need it below that let's open up a div with a class name of relative and a background of white inside of here one more div with a class name of MX Auto so we're align it um horizontally by itself and a Max width of 7 XL and a padding X of eight inside of here let's create another div with a class name of grid a grid D calls D4 a gap X of 8 a gap y of 10 and a padding y of 16 there we go and one final div in here with a class name of call- span D4 a call- start D1 a grid a grid - calls D3 and a gap X of eight there we go perfect inside of here we're going to display the category content so for example let's take a look at what that means let's go up quickly to the product categories this is the content that we want to show up when we open a category in the nav bar so that's what goes right here in the nav item and we can simply call the category do featured. map so we're mapping over each of these objects right here that's what we're doing so for each let's call it item we're going to return some jsx right away so the round parenthesis and this is going to be a div again because we're mapping we need a key this is going to be the item. name and let's give this div a class name of group of relative a text-base for the font size and on small devices end up a text- small so only on mobile devices this text is going to be just a bit larger so it's easier to read perfect inside of here goes a div with a class name of relative an aspect Das video so it's 16 to9 if we hover over this right here you can see that an overflow Das hidden a rounded Das large a background gray of 100 so a very subtle very light kind of grayish tone and on group minus hover so whenever we're hovering the element we assigned the group to this parent div right here then parentheses we're going to say opacity minus 75 just like that beautiful let's uhu format this and quickly go into full screen in case you got lost anywhere and this is the entire class name and inside of here we're going to create an image element that we going get that we are going to get from next / image which is automatically optimized very very convenient for us um because the page will load faster it will perform better in you know Lighthouse and other performance metrics all done for us by this nextjs image component as the source we're going to use the item. image source what we earlier dragged over from the project remember that live in our public folder these images right here that's what we're using as the source for this image as the alt tag let's say product category image let's give it a fill property so it will fill up as as much space as it possibly can and let's lastly give it a class name of object D cover and object D Center behind the closing tag of the image and below the closing div right after it let's create a nextjs link component that's actually going to take us to the category that we clicked on this link always expects in hre and this is going to be the item. hre currently these are only hashtags if you remember but we're going to change that later so we get actually taken to to the relevant product page this link gets a class name of margin top 6 a block font D medium so it sticks out of bit with a higher font weight and a text Gray of 900 so pretty dark inside of this link let's open it up goes the item do name right so it's whatever wait let's check this out let's take a look at the product categories there we go the name right here so editor picks new arrivals best sellers that is what we are putting right here inside of the link as the kind of thing to click on right below that let's put a ptag saying shop now and let's give it a class name of margin top one and because this is not really necessary for screen readers we can also add the area hidden of m true because this will be totally enough for any person that uses a screen reader perfect let's save that and let's see what happens let's give this um a lot more space let's just go into full screen and we can already kind of see wow this works the images are getting loaded perfect we can see the Navar content and of course it looks a bit weird cuz I'm so zoomed in and but we can see all the stuff works really really nice because it's the first time that these images get loaded they take pretty long um but that's fine as soon as they're loaded we can switch around and they're instantly there and if we now click on this right now nothing will happen CU we haven't put the links yet but that looks really really nice as for the menu however one thing that currently does not work what users of course expect to work is that if you click somewhere else on the page the Navar doesn't actually close you have to press this button again and that's not very user friendly we want to change that and we can do that right here in our code instead of the nav items in the plural that contain all the individual um elements and inside of here the first step we're going to do is declaring a ref so we know if we're clicking outside of the element or not let's call this const nav ref is going to be equal to use ref and I know this is a tiny detail but things like these really matter to your users this is just expected to work in any good app and of course we're going to write really good code for this project so that's why it should of course work we can import the US ra also from react cuz again I want you after this video to be left with a proper good project that just works you know so it's it's really important to me that stuff like this that you expect to work especially as a user um you know just does for the user ref we're going to say HTML div element as the type this is going to take on inside of the generic if you're not too familiar with typescript don't worry essentially we're just letting react no hey we will assign this to a div element later and by default this is going to be null so let's initialize it as null now for the functionality of when do we know if we're clicking outside so for example um right right here when do we know that is happening and for that we're actually going to use a custom hook and there's a website I don't quite remember the name react hooks is there react docs there was a website that is specifically for react hooks I don't quite remember the name and but from that I got a hook and that is called let's go into here into the GitHub that is going to be called anyways doesn't matter um I put it into the copy paste list because this is not a hook you write yourself but instead you know I just copy it over from Project to project and copied it over from that one website as well I didn't write this myself and the beautiful thing about not having to write this hook yourself is that it doesn't use anything external it's pure react so it just works out of the box and let's insert this hook into a source folder into a new folder called Hooks and let's call this hook use- on- click- outside. TS and paste in this Hook from the copy paste list of course we're not just going to stupidly copy paste anything let's see what this does actually let's understand it so what this does is essentially it allows us to pass in a ref the ref we have just created as the nav ref is what we're going to pass in here and then also a Handler what should happen if we're clicking outside of this element and all that's happening in here is we're attaching document listeners that listen to touch or click so it works both on mobile or on PC and if the element that we clicked is not in the Target that is all handled right here then we are clicking outside and the Handler will be called so that's what this function does might sound a bit abstract but the usage is incredibly easy let's see how this works so right below the nav ref for example right here let's say use on click outside and for the ref we need to pass in the nav ref and then remember the Handler now what should happen when we click outside of the nav bar of course we want to close the nav bar and we do this by setting the active index to null just like this perfect let's see if this works let's save the entire thing let's give this a tad more space and why isn't this into a split screen view again there we go let's give this a bit more space um and let's click outside okay interesting currently it doesn't work the thing is not closed yet so let's see what's wrong let's minimize this again ah the problem is right here while we are using the nav ra not assigning it to the div that we want to track a click outside of so to do that let's call a ref property on this div and just pass it the navre let's save that and now it should work let's go into a bigger view restart this and now let's click outside and automatically the nav bar will be closed perfect just in terms of accessibility though it's also important that we close this when pressing keys like Escape for example the Escape key is a general you know generally understood go back back key and we also want that to work with our Navar of course users expect this to work and to do that very simple let's create a use effect and again the AO Imports are not working so let's quickly restart the window and then we should be able to import the use effect from react let's give this a second to load there we go now it works and inside of this user fact this takes a callback function and a dependency array um but we're not going to worry about that just for the function this will do let's declare a const Handler this will be an arrow function just like this and take in an event right here and this is going to be of type keyboard event and if you don't know where this type comes from I'm going to show you here in a second that this doesn't just come from anywhere you know and if the E do key is going to be triple equal to escape so if the user presses the Escape key then we want to close or nav bar once again we already know how to do this just by setting the active index to zero or to to null not to zero so let's say set active index to null cuz if we set it to zero it would actually be um the first element right here right we want it to be null so no element at all now this Handler let's also use it you know and we do that by saying document. addevent listener and we're going to listen to the key down event and when that key down event is fired then the Handler will get called now as for where this type comes from cuz if you don't know we can simply get the event from the function hover over it and we can see by default this is going to be passed as the keyboard event type that's how we know that this event will also be the keyboard event because essentially the syntax is the same as this right but the short hand is just saying the Handler but the event will still be passed in and just the same as if we actually had the Callback function with the event right there so it's just a little Shand to just use the Handler um it's just more readable it just looks nicer and that's why we do it as for the cleanup function uh let's say document. remove event listener not remove child remove event listener there we go and this needs to match the exact same thing as up here so the key down event and also the Handler so for the clean up we are preventing memory leaks when this component unmounts by cleaning up after the use effect perfect let's see if it works let's open up the page let's give it a lot more space reload everything so we get the freshest version let's hit Escape perfect that closes the Navar if you click outside that closes the Navar beautiful job that behaves exactly as users would expect it's accessible through keyboard through Mouse clicks really really good job if that is not a beautiful Navar I really don't know what is you can be very proud of this very very good job okay really really nice job if we navigate over back to the Navar right here one thing we did not do yet is the mobile nav we haven't done that yet at all and one other thing we did if you take a look at the nav bar right now it looks really really good however there is the complete right side missing so let's draw this out let's go to excal draw just any you know drawing tool I'm going to show you what the Navar oh what's that that's old we don't need that uh what the nav bar should look like so if this is the nav bar nav bar there we go let's kind of sketch this out how we want it so for example right now we have the logo right here on the left hand side let's turn that blue and fill it out and why doesn't it fill there we go that's the logo we're going to have the categories right next to it the UI kit for example or the icons and this is just uh for your understanding of what were um what we're doing what we're building here and then on the right hand side there should be three things so for example we want there to be a sign in button very very important so users that you know are already using that can sign in then arguably even more important a create account button um I'm going to do a little line break here because it barely fits into the nav bar there we go and then we also need a cart button also very important like a little um cart right here that users can click and see their current um card content and that's going to live on the very right side of the Nar and this whole thing is still missing we didn't do that and that's going to be the final piece for our desktop navbar to look amazing so that's what we're going to do right now I think it's really important to not not just learn how to write good code but to also understand you know what we're doing and why we are doing um what we are doing so that's very very important okay let's do this let's go below the this if containing the nav items right here and with two more diffs go to the max with wrapper this is where we're going to code out the right part of the Navar starting with a div this div will get a class name of ml Auto that's going to push the entire content to the right hand side exactly what we want a flex and an item stash Center inside of this diff let's open up one more diff with a class name of hidden a large Flex a large Flex D1 so it takes up as much space as it can inside of the flex container on large we want an items Das Center on large we want a justify Das end and on large we want a space X of six there we go let's open up this div and give it a tad more space in case you missed anything in the class name here it is in its entirety and inside of here we want to do a conditional check so let's say there was a user right right let's give the Navar a bit more space if there is a user so if somebody is already signed into our app there is literally no point showing them the sign in option or showing them the create account option because after all they are literally already signed into app they are a user and so there's absolutely nothing to gain from that however we haven't implemented o into our app yet that's going to be the very next step just to complete the nav bar for now though let's quickly mock a user at the very top of the file mocking something is something you're going to see for example in testing um it's very very common essentially just pretending you have some data that you really don't for example let's say cons user is equal to null to indicate that there is currently no logged in user to our app of course again there can't be because we didn't do o yet but just for the Navar it's going to make sense to mock this out so we will have all the conditionals in place so that when we later implement the actual o we're going to have everything already implemented out of the the Box you know so let's open up this um div right here we created the uh starting with class name hidden one and inside of here we want to do a conditional check if we have a user in that case we want to render out null so literally nothing and if we do not have a user if nobody is signed in in that case we're going to offer them the option to sign in using a nextjs link component these always take in hre and the hre will be to slash sign-in that is where our sign in or login page whatever you want to call it will live and let's say sign in inside of this link component of course this would be unstyled let's save it and take a look at the desktop version and it doesn't even appear let's give it a bit more space and zoom out there we go on the right hand side we can see the sign in button but let's be real here it looks absolutely garbage uh cuz we didn't style it yet so let's give it a class name and again very neat trick we can just use our button variants from our UI library and if you want to specify this by default it will be the blue button you can see that right here however we don't want that that's a bit too much instead let's give it a custom variant by passing in an object right here and as the variant we are going to choose ghost so a very subtle kind of color um for this button perfect right below this conditional check we're going to open up one more conditional check and if we have a user again we're going to render out nothing null and if we do not have a user in that that case we're going to render out a span element this span will get a class name of height six a width of PX for one pixel a background gray of 200 and we can also give it an area- hidden of and that's going to be true and this span can be self-closing we're not going to put anything inside of it it's purely decorational and that's why we also added this attribute that I explained earlier for screen readers it's like a visual separator between the element we're going to create next and that is going to live right below this conditional check once more a conditional check if we have a user in that case we want to render out a little drop down later that's going to contain all the user information like their email the option to log out and whatnot we don't have that currently so let's just leave it blank for now and as the alternative we're going to render out another nextjs link element and we're not going to get any autoc completion so let's just put anything in here like a P tag for example um of course we're going to change this up later and then as the alternative if we do not have a user that's where the link tag will go this of course always takes an HF and this is going to lead to slash sign up or register page if you want with the same class name as above so as the class name we can pass it the button variance and we don't want the big blue bright button but instead the variant is going to be ghost for a very subtle kind of um button color and inside of here let's say create account awesome let's do another conditional check right below this I know a lot of conditional checks the authentication is going to be a super important part the entire purpose of all these conditional checks is to make sure that whether the user is logged in or not they always see the relevant options for them super super important thing in our app of course we don't want to show the sign in button to already signed in users you know and we want to show the log out option to people who are already signed in and we can only achieve that through some smart conditionals like we're doing right here let's open up this conditional by saying user and if we have a user in that case we're going to render out a span element and if we don't have a user then we're going to render out no so it's the other way around now this span element we can literally just copy and paste the one from above it's the exact same separator so let's just grab that and paste it right in here um if we have a user and right below that we're going to do another check a conditional if we have a user then we're going to render out null and else we're going to render out a div element this div is going to get a class name of flex and on large devices a margin left of six and inside of this diff of course we still need a closing tag for it inside of here we're going to put the exact same span element one last time so let's open up the div paste the span element right in here and save it perfect that's a very very large part done you will see the um little separator right here because one item is still missing and that is actually the cart icon the cart item um in the Navar that will show the user how many items are currently in the cart and also provide the option to buy the items right away and these are going to live right below the last conditional check in another div element with a class name of margin Deft 4 a flow Das root um essentially putting each element in its own little block you can read up on it if you want um it's a property you rarely use but when you need it it's very useful and on large devices and up a margin left of six and inside of here we live the card component and of course this component doesn't exist yet but it's going to be one of the most fun components in the entire app because it's so important to any e-commerce application every single one of them has a card component and you're going to see it's going to be very enjoyable component to write again it doesn't exist yet so let's head into our components folder and create a new file called cart. TSX and one of the reasons this component will be so enjoyable to right by the way let's get started with it let me talk you through that in a second con cart is going to be equal to an arrow function and of course we need to export that as default at the very bottom of the file and return anything for now again one of the reasons this card is so enjoyable to write is is because we're going to be using our UI library for a very large part of it so let's quickly open up our CMD or command line and type in npx shat cn- UI at latest add and this is going to be called sheet there we go hit enter that's going to install the sheet component and while it does let's head over to shaty Nui to see what we are expecting to happen let's take a look at the sheet component and let that run in the background so we can see what it does in the meanwhile if we click this open Button which essentially will be our cart icon then on the right hand side a sheet a little you know I guess you kind of could call it model a sidebar I guess would be a better name pops up here on the right hand side which is going to contain or card items it's fully accessible it looks really really good out of the box and we really don't need to do anything for it we just need to install the component let's start back up our Dev server and really is it's as easy as that we can already get started in writing this component now for example at the top level of the card we're going to return the sheet we just installed from our UI library and by the way one thing we also quickly want to add to the very top of the file first line in the file is the use client directive as before because we're going to need some client side interactivity in this component perfect as for the trigger what we need to click for example this open button for the sheet to open this is um inside of the sheet trigger component that the same component the same UI Library provides to us that we can simply put anything in like a shopping cart icon from Lucid react that's what we're going to use and let's give this shopping card a class name to make it look better that's going to be height of six a width of six a flex shrink of zero there we go a text- gray- 400 format this by the way so we have a bit more space and on group- Hover we're going to give it a text- gray- 500 so a a bit darker now the group hover of course also needs a group where does this come from we didn't declare that yet that actually goes on the sheet trigger let's give this a class name that is group so if we hover of the sheet trigger then by adding this line right here the sheet uh or the the shopping cart will know that it should highlight so we don't even need to hover over this element we can also use this element as a hover trigger to change styles on this one that's a really powerful thing that Tailwind allows us to do with these groups it's very very nice anyways after the group let's say minus M minus 2 for a negative margin a flex an item stash Center and a P2 a padding of Two Perfect the shopping card can also receive an area- hidden of true just for some improved accessibility Trust me if you want to write good code you better make it accessible it is very important after the shopping cart or below let's create a span element with a class name of margin left 2 a text- smm for small font D medium a text Gray of 700 and we also want to add a group Das hover so we are also listening to the same group element as the shopping cart and on group hover what we want to happen is a text Gray of 800 just like that perfect inside of the span element later we're going to display the amount of items that are going to be in the shopping cart for now again we can just mock this mocking just means an example number that will represent kind of like the final state of course the final state is going to be dynamic and actually contain the correct amount but for now just so we can style it correctly let's leave one and as for the sheet content this is going to live right below the sheet trigger so essentially what is going to be in the little sidebar that pops up that's what we're doing right now in the sheet content let's give it a class name of flex a width of full a flex-all a padding right of zero and on small devices and up a maximum width of large inside of the sheet content we can now Define for example these boxes right here but of course we want the products that are in our card as the sheet header we can give it like a little headline um let's create that and give it a class name of space y of 2.5 and a padding right of six inside of the sheet header let's say card and then we can say for example in parenthesis zero for now of course we will change this later to the correct number of items that are in the card perfect and just to make everything look a bit better let's take this card and the number and actually wrap it inside of something called a sheet title that's a separate component we still get from the same UI library and that's going to make this look good out of the box by the way we can already take a look at how this looks like let's save the card go into our nav bar and simply import that card component it doesn't take any props so we can just import it as is and as long as the dev server is running we should be able to see the card um right here on the right hand side in the nafar so let's reload the page and if we click it we can see then the card zero shows up with a closing icon and already this is fully accessible we can click the closing button we can hit Escape whatever we want and it just works and looks beautiful fall out of the box beautifully smooth animated it's a really really nice component by the way we can close out of some of these files let's stay in the cart and quickly get this done because honestly there's not so much left inside of the cart awesome let's continue right here oops didn't mean to do that right below the sheet header and let's mock out one more value and that is going to be the cons item count at the very top of the card that Slater going to dynamically contain the amount of items in our cart for now let's just put it to zero so we can get a get ahead with the styling it's just going to give us a much easier time because we're going to do a little conditional check right here in or below the sheet header if the item count is larger than zero we wanton to render out some different jsx compared to when it's you know empty because when it's zero then the card is empty and we want to display a little image that the cart is empty if the cart is not empty however in the first case let's render out a y fragment and the first thing we're going to put in here is a div element with a class name of flex a with of full a flex-all and a padding R of six and inside of here let's just say um you know card items we don't have any product you know logic in our app yet so it doesn't make sense um to try to display anything here yet let's just put some text in here and also add a little to do common so we don't forget to do this later um card logic just to make sure when we're done with this app you actually have a complete um app and we didn't forget anything awesome right below this div element right here let's create one more div with a class name of space y4 for vertical spacing and a padding right of six inside of here we're going to put something called a separator and this is just a purely visual element so everything looks better we could easily write ourself or or UI Library already provides this out of the box so let's quickly say npx Shad cn- UI at latest add and then separator that's going to install the little separator component again super simple component we could easily write that ourself but it's already in there it looks good out of the box separator let's take a look at it you know just looks like this pretty straightforward and then let's start up or they have server again so we can just use this component close out of the console and import that from our custom um UI Library Perfect all right below the separator another div and let's give this a class name of space y 1.5 and a padding right of six and by the way just to get rid of the syntax error for now let's replace the empty parenthesis down here in the other case um with a div element for now just so we can already format this whole file and we don't get annoyed by prettier okay below the separator in the div that we just created with a space y of 1.5 let's open it up create one more div in here and let's give it a class name of flex inside of this div let's create a span element with a class name of flex D1 so it takes up as much space as it can saying shipping these are going to be the shipping costs right below that one more span element and we're going to say free because of course we sell digital products it doesn't cost us anything to ship on by the way this padding right of six is going to be a text small not a padding right of six so it's space y 1.5 and text of small perfect right below this div we can actually just copy it down the flex div with a two span elements inside of it let's copy it down using shift alt and arrow down we get the same element once again and let's say instead of instead of shipping transaction fee and the transaction fee is going to be $1 and instead of just doing something like this and typing in A1 which would kind of work I guess for now if we reloaded the page let's see what happens if we try to do that and just type in a literal number and why is the card not opening let's click this there we go oh the content is not showing up by the way because okay let's say we have one element in the card then it should actually show up because right now the conditional check is failing and we um don't even see that stuff so let's reload the page click the card there we go the ship is there and the transaction fee is also there but literally just a one doesn't look good we want to properly format the values we have in our app in dollars for example or in local currencies and to do that we're going to create a really helpful utility function that's going to live in our lib folder under U.S or UI Library already created this CN helper function as I um told you about earlier and right below that we're going to say export uh let's call it function and then format price this is going to be a very helpful function throughout the entire app because we're going to be working with numbers and prices a lot and we can always use this function right here to get the formatting right across the entire application so it always looks the exact same as the arguments or parameters as the parameters we're going to receive the price and this is going to be either a number or a string as the typescript type and we're going to receive some options these options are going to be an object and this can be the currency there we go which is optional and as the options we want to allow for currencies this is going to be USD for dollars or a euro currency or a GBP a Great British pound or a BDT there we go and one more thing we want to allow is the notation and this is going to be of type and by the way this is also optional that's why we have a question mark right after it it means we don't have to pass it but we can and SD type this is going to be an intl. number format options at the index of notation there we go so we can just get the typescript type of this at the index of notation in pretty much the same syntaxes if we were you know accessing like one element of an array in JavaScript for example and we can set this to empty object at the very start okay for the actual function content first off we want to destructure some things and that is going to be the currency and let's default this to US dollars so unless we change it by default it's going to be USD and the notation which is going to be defaulted to compact just S A String Compact and this is going to be equal to the options so we're destructuring the currency right here and the notation and setting the default values just like this okay first of we want to ensure that the input is actually a number so let's say con numeric price is going to be equal to and then do a Turner check if the type of price is triple equal to string we know it's not a number so in that case we want to turn it into a number and we can do that using the pars float and just pass in the string of price and otherwise if it's not a string then we know it's already a number because it can be either one or the other so in that case we can literally just um take the price as it is and as for the logic it's very straightforward let's return a new intl. number format and as the locals as the first argument we passed in here this is going to be en-us and as the object we're going to pass as the second argument this is going to contain for example the style and this is going to be currency as the currency we can oops currency we can pass in or default currency unless we change it earlier by the way this is just a short hand for this so we can say currency you know C and currency or in JavaScript typescript you can just have a short hand and just pass the currency if whatever comes after it is the exact same as what comes before it right here we can pass in the notation and let's get rid of that colon right there and lastly we're going to pass in the maximum fraction digits and that is going to be two so we can ensure that only two decimal places um are going to be displayed later and on here we can call the dot format and pass in the numeric price just like this and that's already all the logic and take a look at what this does right after coding it out let's see it in action so instead of the one that we had earlier for example let's say format price one so we're going to use the utility function we have just created format Price save that and let's go back into our cart and see what happens if you take a look now it says $1. so it looks exactly as we wanted to and that's going to make it super easy to work with prices across the entire application let's not hard code the one right here though instead let's call it fee and declare it at the very top of the card con fee is going to be equal to so one just so um we can reuse the same constant across this whole um component because we're going to be using it one more time right below here let's copy down the flex div once again one last time and this is going to be instead of the transaction fee or the shipping the total of the card of course we need to calculate the total and the total is going to consist of the fee and then all the items that are currently in the card of course we don't have access to that number yet yet because it's going to be dynamically determined later so let's leave it out just for now and continue two divs down so this closing div after the span one more and let's open this up right here and the last thing we want to do right here and why did I switch keyboards again is going to be the sheet footer just like this inside of here we're going to put a sheet trigger once again and this is going to receive a property as child and what that means is basically by default this will create an entire button element so whatever we wrap in it will be wrapped in a button element with the as child we can disable that default behavior and provide our own element in our case a link component that is not going to be wrapped in an additional button CU for the link there is really no purpose in that as for the h of this link this is going to be to slash card perfect as for the class name of the link to make it look good let's say in curly braces the button ver Varian import that and inside of here um we can provide it a custom class name so we can overwrite or um not really override but add to the defaults and this is going to be a width of full perfect inside of the link let's say continue to check out so this is going to be the big button we want people to press later on and of course one thing that we didn't handle yet by the way this looks really good and fully responsive already with a card very very nice um even if it's not meant to be opened like in the small view um it's going to be a bit different later on um it already looks really really nice good job um one thing we didn't handle yet if the item count was Zero for example if the card was empty currently that would look really weird to users they wouldn't really know what's going on so let's handle that case let's give this diff a class name of flex a height of full a flex Das call an items D set Center a justify Das Center and a space y of one let's open up this div element and create one more diff inside of it with a class name of relative a margin bottom of four a height of 60 a width of 60 and a text- muted D foreground there we go inside of this div we're going to create an image element and this image is going to be really really cool it's going to be self closing and as the Source we're going to say in hardcoded strings this is going to be hippo D mt- cart. PNG an artwork that I will completely provide to you you can just get it from the giab repository we're going to get to that here in a second a fill property so it will take up all the space we give it and lastly an ALT tag and this is going to be uh you know empty shopping card hippo or whatever just to kind of describe the image by the way because this again is purely decorational good practice is to add an area hidden of true um to this property because it's purely decorational as for the actual image the empty card currently of course we will get like a little message that it doesn't exist and it will show the alt tag so let's go into our public folder and see where we can get this image from this image lives right here in the um giab repository let's navigate back and into the public folder right here because this this is where all the images live for example the um which one is this the hippo art.png there we go this image right here if you click on it you will be able to kind of preview what it looks like it's a really nice hippo so go ahead click this button up here to download this image and put it with of course the same name that you're referencing right here um in the image into your public folder not into a sub folder just the public and I'm going to do that right here with you and download the image drag it over the hippo empty card into the public folder perfect let's go back in our app once we have that and then reload the app right here and see what happens if we open up the card there is the hippo with an empty card that looks really good really really good awesome and now let's actually put some text right below it that's going to complete this um so right below the closing div after the image let's open up one more div with a class name of text - XL and a font D semi saying your card is empty empty there we go after this div let's create a sheet trigger just like before with an as child property and this is going to contain a link element as well this link has an HF and this is going to lead to slash products and also the link to make it look good will get a class name in these curly braces once again you know the drill by now or button variants so it doesn't look absolutely like hot garbage as the variant we're going to pass in link that's a you know own variant to make it look good out of the box as the size we're going to pass in small and as the additional class names we can add to the defaults this is going to say text- small and text muted of foreground a class that or UI Library provides to us inside of this link let's open it up and say add items to your oops to did I change keyboards again added to your card to check out perfect so when you click that sheet trigger then you're going to be taken to the products page and that almost concludes our shopping cart of course the dynamic values are not there yet that's what we're going to work on next um but this looks really good already your card is empty at items to your card to check out when you click that you land on well later the products page that doesn't quite work yet um but that's a one of the most beautiful empty card images and states that I've ever seen I think we're doing a really really good job all right and the next step is going to be one of the most important in the entire build because that is going to be the dashboard the admin dashboard where admins go to verify products to see who ordered what where users go to sell and where buyers go to see all their orders essentially the admin dashboard where everything in the backend at least is managed very very important part not only that but you're also going to learn how to completely self-host nextjs because it turns out any server that can host nodejs you can use to self host nextjs with all the features it has so it's a very very good skill to have because it makes you completely independent of theel platform you can deploy it anywhere and I'm going to show you how and actually it's not very complicated people think sometimes it's very complicated it's really not let's get started by creating a server. TS inside of or Source directory that's going to be the entry point for the express server we're going to use to self Host this entire thing now to get started with this to self host or Express server we first need to add Express to our apps so let's say yarn add Express and hit enter that's going to install the most important package to do all this which is Express I mean I I I don't need to explain to you what Express is and we can simply import Express from Express in the server. TS file and it seems like Express is not um you know coming with its own types so we need to install those separately let's go back into our console and say yarn ad minus D for a Dev dependency we don't need it in production the at types SL Express and that we need to do this with typescript okay first off let's define our app the con app is going to be equal to express and invoke that and let's also Define a port in all caps loog because this is a constant that doesn't really change so either we have or let's set this equal to either we have as a number the process. env. port by default a process. EnV is always a string so we need to convert it to a number or we're going to use 3,000 if that is not provided in production this will be provided in development we can just default a 3,000 because in production the server where we host it will give us the port where it will be hosted on so we want to use that of course and not or 3000 and then let's define a con's start as an async arrow function there we go this is essentially the big function where our server will you know start up and let's already start the server let's call the start function um at the very bottom right here okay the first thing we want to do inside of this start function is actually start up or CMS or you know admin dashboard that's what it is and we're going to do that by saying const payload because the admin dashboard is provided by payload CMS make it super easy to manage all the stuff is going to be await equal to await get payload client and in many ways this is super similar to like a regular database client as you're going to see later we can use it for querying data for changing data for finding and deleting data for all the same that we can use a regular database for all with typescript by the way fully types saave it's really really nice to work with and let's create a new file that will you know get as this payload client for example let's call it get and then I change keyboard again there we go get- payload dots that's going to initialize or CMS in this file right here let's say export cons get payload client just like this and this is going to be an arrow function and we're going to turn it into an async error function because we're going to do some async stuff in here however before we do that let's go to the very top of the file and say import EnV from EnV because we're going to need access to some environment variables inside of this file now EnV is a package we haven't installed yet so let's do that yarn ATV that allows us to use these um did we already have a EnV in this folder no we don't okay we're going to create that together essentially where we can put secure variables and so we can use them in this file that's what this dependency Dov right here is for and we need to tell it by saying env. config and inside of here goes an object where we tell it the path where our EnV file the file that contains or secure your variables will live and this is going to be under a certain path and to get that path let's say at the very top of the file import path from path it's a builtin njs thing we can just use and for the path for. EnV we're going to say path. res oops resolve and inside of here goes the underscore uncore durame so the current directory name that's what it means and we can join it together with the dot dot slash so One Directory back Dov so because this is in the source folder essentially we're saying take the current directory go back One Directory into you know the main directory where everything else lives and then into the EnV file it doesn't exist yet but that's Creed Dov right here this is where secure variables will live perfect now to get our client we want to make use of caching to you know just save resources especially in production that's a very good idea let's say let cached is going to be equal to and then a global oops Global as any do payload if that exists then we know we can just use the cached version if it doesn't exist if not cash in that case let's handle it for example let's set the cache in that case so next time this happens we will have it cached so if we don't have a cached version of our CMS in that case we're going to say cached oops cached is going to be equal to Global as any. payload and this is going to be equal to an object containing the client and this is going to be null and then the promise and this is also going to be null so next time around we can also use the cash version and just save some resources on it now as for the much more important part the get payload client that will get us access to our you know database and all that where we send emails log users in hand authentication and manage products and whatnot that is where the get payload client that's what it's for and we're going to receive one thing in here as a parameter and that's going to be the init options that we can destructure right away so in this object syntax right here and we need to tell typescript this is going to be of type arcs now these arcs the arguments don't exist so let's say interphase arcs and this is where we can Define what will go into this function for example the in it options and this is going to be optional so we don't have to pass it that's why we put a question mark and this is going to be a partial which is a typescript utility type this takes the init options right here which is a type that or CMS payload provides to us however we don't get it right now because we don't have the package for payload installed so let's say yarn at payload right here in or CMD hit enter and let that load for a second and then we can just get the built in type from payload for the there we go for the function that's where we need it by the way payload again is the sponsor of this video and if you're getting value out of this video we're going to build literally this complete project in this one single video together then you know check out the link in the description maybe it's a tracking link so they can see how many people clicked on it and came from me and it would really mean a lot because that's you know all I'm asking this video completely free everything am completely free for you and who knows they might just want to work with me in the future um if they see that a lot of people um come from my video to their product of course you don't have to um but I would really appreciate it okay anyways let's continue and um let's now import these init options so typescript is not very mad at us let's say at the very top of the file import type so we just need the type this is going to be init options and we can import this from payload SL config just like this we can get the typescript type typescript will be very happy with us and now we can actually initialize the arguments as an empty object so just the default value as what's going to happen inside of the get payload Cent well first off if we don't have a process. env. payload Secret in that case we want to throw a new error saying for example payload secret is missing this this variable is so important because we will use it to sign all the authentication stuff for example the Json web tokens if it's missing we can't do that and you know our application just won't work this is a very very important thing that we need and we can very easily create it if we navigate over Tov and say for example payload secret secret is going to be equal to and then my secret or whatever this really doesn't matter what you put here but it should be much more secure um than what I'm putting right here because again this is used for signing authentication stuff and this should be a very secure um string so just uh either Mash your keyboard and type in something secure or you you know even go as far as doing a SSH key gen or whatever or just uh put in something stupid it doesn't really matter if you want to deploy it you better choose something more secure okay with that said let's say if we have a cached oops a cached cached there we go do client in that case we can just use the cach client let's return the cache. client um so making use of the cache and if we don't if we don't have a cache. promise so this one from right up here in that case let's set it the cach do promise will be equal to payload do init now where does this payload right here come from that's very simple we can simply import payload from payload at the very top of the file that's going to give us the init function this actually takes a configuration object for the initial initialization that we can configure for example the secret and this is going to be our process. env. payload secret that we have just finded we can give it the local and this is going to be the init options. Express there we go and if a init options. Express is passed in that case we're going to return false for the local and else we're going to return true and for the rest we can simply spread it all in so we can say dot dot dot and then in parentheses the um the init options or if there are no init options than just an empty object just put everything else in we don't really want to worry about it perfect right after here we're almost done with the get payload client and we're going to have a try catch block the error is going to be of type unknown and I'm just going to call it e you can call it error or error if you want it absolutely doesn't matter in the tri block we're going to say cash. client is going to be equal to await cached do promise and in the catch block if that didn't work we're going to say cached do promise is going to be equal to null and we're going to throw the error so we can catch it somewhere else and the last thing we're going to do in this file is just return the cached do client okay I know rather abstract right what really did we do essentially overview of this file we created a database client that we can now use in the entire application it's going to be very very important and we're also made sure to cach it so we can optimize you know our resources that's basically the high level overview so what that allows us to do is to just import this get payload client everywhere where we need databas database access from now on for for example in or server file and we can pass this get payal client as we just defined the init options now this takes the express for example this is nothing else than the app we have already defined at the very top um oops at the very top right here and this also takes something called the on init and what we can do on init is going to be asynchronous and we get past our CMS into here as kind of like a callback function and in here we're going to say in the arrow function cms. logger doino and we're going to log out the URL where or admin dashboard will live for example let's say admin URL in these template strings so we can now interpretate values which is just a fancy word for putting Dynamic values inside of a string for example cms. getet admin URL so for example in our digital hippo app this will be localhost 3000 cell or later in the deployed version like or deployed URL SL cell for example and as easy as that we now have a working database client that we can use anywhere in our entire app and to use nexj for all the logic and the pages and whatever we are going to define a new file right here in our source callede next- utils a very small file very simple in which we're going to Define some utilities for next JS for example let's say the con port and we can do this in caps again is going to be number par this into a number the process. env. port for production or if we're developing locally that environment variable won't be set let's default it to 3,000 and we're going to say export const next app so literally or nextjs application that's what it will be is equal to next something we can import from next and this takes an object and inside of here we can say the dev is going to be so if we are in Dev mode or not the process. env. notore EnV is not equal to production there we go and we can also just pass in the port and this is a default node EnV this will always be set in production or locally it's just always there for us and as for the Handler that's going to handle oh and this needs to be caps by the way as for the Handler and this needs to be Port there we go okay again as for the Handler where all the nextjs logic is going to be handled we can simply export a cons next Handler and this is going to be equal to next app.get request Handler so if you want to self Host nextjs this is how you do it we can simply forward all the logic right there to nextjs by using this little utility right here for example let's try it out in our server for all the routes all the logic we're just going to forward that to nextjs by saying next Handler import that from our utilities we just created and we want this to handle all the requests and responses now where do these come from the request and response so we can forward them to nextjs well let's cut this for a second using control X and let's say app. use so an Express middleware that we are creating and for each request and response that we get in Express we can simply forward it to nextjs just like this it's as easy as that people make sometimes self-hosting out to be something incredibly difficult and it's really not and just like this we can make ourself completely independent from the versel platform and work with stuff that serverless environments normally don't can for example um you know stateful things like websockets okay let's say next app. prepare that is something we can call and of course we also need to import the next dress application and once that is prepared we can call a do then function that takes a call back and inside of here we can say payload do logger doino and we can log out something like um next.js started this is just going to be for us in the console so we can kind of see what happens and then we also need to say app. listen this is a very standard Express thing for example the port that we want to listen on in production that's going to be given to us or 3,000 for us right now and once we are listening we get an asynchronous callback right here in which which we can say something like payload do logger doino and instead of a template string let's say next.js app URL colon and let's append the nextjs app URL for example that's going to be process. env. nextore puor serverurl perfect okay now to make this work and actually let our next J application start from our custom server cuz right now it won't because our Dev command is just next Dev and we want to start it from our own server from our custom server by the way we can close out of all the others except the package.json for now and this is where we're going to stay just for a minute here inside of the dev script what we want to change to instead of from the regular next is boot from our custom server is let's say cross-v it's a package that we're going to install so let's go into our terminal and say yarn at cross. e-v this lets us work with the same environment variables cross platform that's why it's cross so for example between express and nextjs so we don't need to handle any additional logic we can just use the same EnV file in both that's all it does very very simple package and we're going to say cross EnV and then set an inline environment variable that's going to be the payload uncore config uncore path and this is going to equal to Source SL and then pay.con dots essentially we're just setting this inline right here to let our CMS know where it can find the config that it needs this file we're going to create together here in a second and we're going to say nmon at the very end nmon is a very simple utility we can check it out on npm that lets us reboot and express server once any file changes are detected so we don't have to do it manually all the time it's a really nice utility very straightforward very simple but nonetheless a super popular package like 4 million downloads and we can just use it by adding it at the very end of our Command right here now we added cross EnV now we need to create the payload config okay so make sure we have this as the dev command and we also have cross EnV as a dependency installed perfect and now let's go ahead and create this Source SL payload .c config dots a very simple file payload Doon oops config dots in our source folder right here next to all the app and components and whatnot folders and this is just going to be some very basic options we're going to give or CMS let's say export default build config in this file right here we can import that from payload SLC config and in here goes an object with a server URL and this going to be or process. EnV that we just used the nextore public server uncore URL or if that environment variable is not set we can just give it an empty string that's fine it won't work but at least we will get a or no typescript error right here as for the collections let's pass an MTR this is actually going to be our orders and products and users and product files and all that later on very very important piece of this build for now we don't really need it as the routes we're going to say um for the and this takes an object by the way the routes for the admin route we're going to you know kind of map it over to slash cell because by default what the CMS does is host or admin dashboard under SL admin but we want it under SL cell just a bit different okay as for the admin property in here we're going to pass an object but I already you want to get rid of this very ugly squiggly line so let's get to the um parts we have to pass in here that is going to be for example the editor and the editor you can actually choose there's two options there's lexical and there's slate lexical right here is a text editor you can use or the alternative by the way these are both for like Rich text and uh slate JS is the alternative I'm more familiar with I actually built some series applications with slate um so that's what we'll be using in this video both are good if you want to go with the other option with lexico that's totally fine and up to you I'm going to use slate editor and I just recommend you follow along um but of course you can also go with lexical now this slate editor will come from a additional package because you can choose so for payload it doesn't really make sense to include both in your bundle if you're only going to use one so that's why it's a separate package we can install under yarn ad and then at payload CMS SLR text- slate and while we are here we can already install the bundler as well at payload CMS SL bundler dweb pack that's going to bundle or backend for us perfect and last thing while we're already here we can add the database adapter let's add as payload CMS SL db- mongod DB for this video we're going to be using mongod DB however with with payload 2.0 they just had a massive you know integration with postgress so if you want to follow along using postgress that is totally cool as well in fact I usually go with postgress for all my projects the reason I think it's easier to go with mongodb is first off because there was one little bug with postgres that I believe should be fixed when this video is out so that shouldn't be an issue and then secondly also what we do is work at work is use a third-party tool called D beaver to interact with or database and I don't assume you have that additional program you have to install it separately and mongod DB just has a web interface which makes everything a bit easier um just for this video of course you can always change it later that's not a problem it takes like 1 minute to do it's very very simple um so let's follow along with DB I encourage you to and but if you really want of course you can use postr now change to it later no problem whatsoever if you're more familiar with that okay as the DB we're going to pass the adapter there we go and this comes from the the pendency we have just installed import and then let's import the mongus adapter from oops from at payload CMS DB mongod DB just like this beautiful this mongod DB adapter takes a URL and this is going to be our database connection URL and because this is a very secure very sensitive piece of information we're going to um access it under process. env. mongod dbor URL and protect it that way and we can put an exclamation point to tell typescript hey this is a value that definitely exists well currently it doesn't because it's not in our EnV but we're going to add it here together in a second or database connection string very very important you should always keep that secret and don't share it with anybody and because that's super sensitive piece of information as for the Slate editor let's say import and then the Slate editor is a named import slate editor named import just meaning inside of this object syntax right here um from at payload cmsr text- slate perfect and as for the bundler we can also just import that as a named import let's say web pack oops web pack bundler there we go from at payload CMS bundler D webpack and just get it from from there and we can tell our admin dashboard oops we already created this admin property right here to use that bundler so let's say bundler web pack bundler and by the way this is only used for the admin dashboard we are still going to profit from all the improvements in NEX S14 and so this is um just for the back end so all the quick stuff that came with NEX S14 is definitely going to be there for us to enjoy perfect while we're already here we can pass some metadata for example as the title suffix we're going to say Dash digital hippo so later on that's going to say like projects uh products digital hippo or orders digital hippo and whatnot as the fav icon we're going to say/ faav icon. i. IO and as the OG image when you share a link to this application we're going to use the slash Thum oops thumb nail. jpack so if you share this application it will look just as good as the um actual application itself perfect okay one thing we want to pass in here is the rate limit option payload does that for us out of the box that's super cool however I found the default to be a bit too low for development so we can increase the rate limit to a maximum of 2,000 this is by default 500 and we can you know just four times that later on in production you can always adjust this but for development I found this to be a much better number than the default and last ly because this is fully types saave let's switch the keyboard again Jesus Christ let's say typescript and for this typescript option this takes an object and we can give it an output file where all our types will live so the entire application is going to be completely typ safe from front to back because this is a typescript typescript first CMS which is really really cool we can just say path and import that from the path. resolve just as we did before you already know the drill then start at the current directory name and then navigate over to the file where we want the types to be put in our case let's say payload D types. TS so it will put all the types generated from our collections like the users and products right into this file as you're going to see later awesome let's save that and that was the Second Step of our development script right we have the cross EnV in place we have the payload undor config undor path in place and now the last thing that we want to do is get nodon and this is going to be a development dependency let's say yarn add- D4 development dependency nmon because we won't need that in production in production the server will start once then there's you know not going to be any file changes for nodon to watch so it's very uninteresting and we can also give not one a very quick configuration that's going to make our life as a developer easier and let's create a new new file for that in the very root of our project called noon. Json and I know we're doing a lot of coding here on the left hand side without anything happening really on the right hand side but something very big is about to happen um on the right hand side as you're going to see because the entire admin dashboard will come to life here in a second okay before that let's finish up the noon. Json very simple file for example let's pass the files that we even want to watch in our case that's going to be an array of strings for example the server. TS we also want to watch The Source SL collections SL and Then star star and then slst star. TS so in any subdirectory we want anything ending ints all the files we're going to watch so that's called a glob pattern actually just you know for your information not that it's super important and the last thing we're going to watch is under Source SL trpc SL index x. TS so we're going to use trpc if you don't know what it is you're going to be very impressed by this tool and if you do know what it is you know the types saave um client that makes API is super enjoyable to write um we also want to watch file changes on that okay let's say exec as the second thing that we're going to pass in here and this is the command we want to execute nmon um to do whenever it detects file changes for example let's say ts- node-- project the project flag and then tsconfig dos server. Json so we're going to have a slightly different typescript config for or backend then for or nextjs front end and we're going to say Source SLS server. ts- d-i honestly I don't know what this does you can probably look it up but it just make stuff work I guess I can tell you what the rest does I don't know what this is for and it doesn't matter because we will literally never touch this file again so if you get lost anywhere here honestly it's not very important as for the extensions we want to run on this is going to be JS and TS and lastly as the STD in we're going to say false again I I honestly don't know what this does I'm not going to pretend I do I don't know what this does what this does you can probably Google it um honestly it absolutely does not matter because we're done with this file and we can just close out of it that's or nmon config done perfect and the absolutely last thing we need to do for our admin dashboard to come to life is to add one more file to our root directory and that's going to be the tsconfig do server. Json and this is just going to be some slight adjustments from our base TS config just for or back end for example let's copy all the values from the main TS config over by using the extends option right here and this TS config for the server extends the/ TS config.js the standard one that nextjs provides to us as for the compiler options we're going to pass a module of commonjs um this is generally you know expected for Express as the outdoor we're going to use this you know which stands for distribut bles a very common name sometimes you call it out but most of the time it's called D then the no emit is going to be false because we do want typescript to emit the files to create create new files and the jsx is going to be react okay last thing in here is the include property and we're going to include both the source SLS server. TS and also the source SL payload doc config dots and that's it let's save that and see what happens once we start up or no Dev or admin server hopefully everything will work and let's say yarn Dev that's going to run or develop velopment script it's going to say starting TS node whatever whatever and let's just see what happens okay missing Mongol DB connection URL that is expected we don't have our database setup yet but that also means that our server is running correctly because it got to that point which is amazing news so all we need to do now is to configure this mongodb connection URL so if you're following along in mongodb let's head over to mongod db.com by by the way again which I recommend I think it's just easier especially for beginners if you know what you're doing and you enjoy post more that's totally cool um but I think for this video I recommend following along with mongodb because that's what I'll be using and you can literally just copy exactly what I do okay let's log in to mongodb I'm going to use hello at joshr coding.com okay after the login let's head over to database right here on the left hand side and if you don't have one yet which you probably won't let's hit build a database and get started by the way mongodb doesn't require any credit card which is really nice and it's totally free to get started with the shared cluster the m0 on the right hand side right here let's select a region that is pretty close for example for me that's going to be in Frankfurt we don't really care about the name and then let's hit create that's going to create our database for us we can create a username and password I'm just going to make them very simple ASD and ASD for the password as well and hit create user and this is important that you remember because that is how we connect to our database later and now I cannot scroll down any more than this because it literally shows your IP and I'm not about to just leak my IP um so right below this you're going to see my local environment right here and then it's going to show a button saying something along the lines of add my current IP that you need to press um so you're able to to connect from your current computer that's very important press it and that means you can just connect to your mongodb database again I can't show this and then let's hit finish and close at the very bottom of the um page that's then going to take us to this screen right here let's put this in a side by side and we can simply go ahead into our database view database deployments and hit connect right here and then under drivers the node.js version of the driver is going to give us the connection string that we need to connect to our mongodb instance um right here okay let's head into ourv file let's create a new one called mongod dbor URL is going to be equal to and then paste in that very long connection string that you just got from morod DB it should look um just like this with a username and password and just put in the username and password you have just created for me that's ASD and as SD once we're done doing that we should be able to connect to our mongod DB cluster successfully so let's try it out let's run the dev command again let's close out of the terminal and let's run yarn Dev and let's see what happens ideally it should say that it connected to to be successfully if we did everything right in the setup and we did perfect that is exactly what we want to see connected to DB server successfully starting payload admin URL SL cell and we do get it type error which is weird we're going to look into that here in a second however the route should still work so let's try it out let's go to Local Host 3000 cell and see if our admin dashboard is there and I hope it will be and it's not seems like the type error might actually be a problem ah and some debugging later I think I know what the issue might be so if we take a look at um or pay. however the next public server URL is actually not defined in our EnV file and this is going to be very simple let's pasted in the ne next public server URL right here this is going to be Local Host 3000 and let's prefix that with HTTP um colon SL slash so payload knows where to host this local instance of our app we can just save that the EnV file and try that again let's say ynf restart the development server and hopefully you should see that payload is now hosted under the SL cell and we can access our admin dashboard there let's see what happens in the console the type errors are still there we're going to take a look at that don't worry that shouldn't happen but I also think it's not a um like a crash reason I think that's fine for now of course we don't want that um there so we're going to fix it but let's see if the um admin dashboard actually works and it doesn't seem like it if we reload the page you can see nothing happens yet interesting let me debug real quick aha so the homepage works now that is being served from our custom server ah and now the cell route works as well so the problem was actually it wasn't anywhere in these config files it was right here it was literally the loggers to be honest I don't know why they work in my production version maybe they Chang something but of course we can just leave it away for now they are just for or information and they're not super crucial I think we might find a very nice workaround later on in the app for now let's just comment out these lines and then we can actually use our admin dashboard and I think I just voice cracked there anyways I'm really excited for this the admin dashboard does work and we can try to use it let's use um please don't use my email please use your email um just put any password and confirm that password and hit create and that actually created our first user beautiful this is our admin dashboard we have our first User it's us we have an ID an updated ad a created ad if you click this we can see change password Force unlock and the entire admin dashboard just works like that that's beautiful I know this was a lot of configuration setup you know with the server with the config with the next utils but again this is a super cool skill to have and I genuinely mean it to know how to self host nexts it makes you in dependent from the verel serverless platform you can use features now that you weren't able to use before or rely on external services on like websockets for example a very good skill to have and the very nice side effect is that we have a fully featured working admin dashboard out of the box using payload CMS beautiful and this is the first step in managing or backend and creating the products sending emails to the users and also handling the authentication I'm really really excited for it so dude great job following along you're doing a really really good job and the next thing we're going to do the very important Cornerstone of our application is going to be the authentication so people can log in using their email and their password and they are then going to be reg registered Hardware as users in or application so they can start listing products and uploading their files start selling and people can also start buying as users as well so that will be the first step we're going to take with the atmin dashboard okay now about the authentication right let me give you an overview of how this is going to look like so xcal draw there we go or drawing tool and then let's see how we're going to do the authentication it's one of the parts that I see beginners be very scared of and it's understandable right authentication is very important if you do mistakes here then yes it could be like like a security vulnerability in the long run that you know could be exploited by users um so let's make sure we get this right here's going to be the off flow so let's say we had a new user right here let's say you for user and let's turn that into a you know kind of like bluish color with a little stroke so the user is coming into our app for the first time ever what happens first we want them to sign up to our services so there's going to be a sign actually we can probably write that inside of here yep sign up page there we go we want them to be sent to the sign up page where they can create an account that is going to be the first time flow when this user comes in and let's give this a little underline there we go okay when the user has enter their credentials like for example the email their email and also their password PW for password those are the two credentials we need from the user what happens then after they've entered that and click the big sign in button we are going to send a verif verification email we don't want anyone to be able to create an account with an email that they don't own so to make sure that they own this email we're going to send them one and that is going to contain a link they can click to verify their um account so let's move that over here once they click this verification email right here they are going to be taken to a page let's put it down here and this is going to be the verifying page so this is going to live under something like verify email for example it's going to be the URL for it and this request right here from the verification email to this verify email page will actually contain something called a token this token will be generated and it will be part of the URL so for example the request is going to be slash verify email and then a question mark to append a query parameter containing the user token for example you know any token here I'm just going to put that in angled brackets in reality it would look something like this like some very abstract um token that nobody really knows what it means but we do so the token is going to be equal to um let's say any token value and using this token right here that is passed from the email we can then verify that the user actually owns the email that we sent this token to and that's going to happen in the verify email right here way later this verify email route is also going to handle the newsletter so that is not even part of the off but imagine a user signs up to our newsletter just as a broad overview right now so you get an idea what that looks like newsletter they enter their email then also a verification email will be sent to the user and the same verify email flow happens right here just with a slightly different URL with an action it's going to be equal to newsletter so we know that the token we are trying to validate here in the verify email actually comes um from the newsletter and it's not an account token because that already needs to exist uh but that's just a broad overview we're going to get into the whole newsletter functionality later once we have set up the off flow so the user comes in goes to a sign up page enters email and password then gets a verification email send right to their email not into the spam folder by the way into the real Prime real estate um main inbox because the service we're going to use for that make sure that will happen and then they can verify their email once they did the account is created so let's put in little arrow down here and we're actually going to create the user in our database and change their verified status so let's say underscore verified is going to be equal to true and implementing this entire flow yourself or with me right now step by step will give you a really good idea of how proper authentication Works in real software you know that people use in production it's it sounds kind of complicated maybe to you but it really isn't it's pretty straightforward once you get the hang and it's actually really cool to implement because o is such an important part and everything or entire off implementation will start at a new page so let's exit out of the server the utils the payload config and minimize everything to clean up the workspace and let's go into our app folder under and let's create a new folder in here this folder is going to be called in parenthesis off and then closing parenthesis now what this syntax does the parenthesis is that this will actually be ignored from the nextjs URL or the nextjs routing but instead we can just organize it for ourself and put some folders in here for example everything auth related like the sign- up folder that we are going to create right now where a file lives that is going to be called page. TSX now it's very important that you name this file page. TSX because that is a naming that is enforced by next JS to create a new page we need to create a folder with the URL name so/ signup and then what needs to be in there is a page. TSX you can't change the name on that and this page. TSX let's declare it con's page is going to be equal to an error function and Export default that page at the very bottom just like that perfect let's return something from this page and at the top level we are going to return a fragment M from this page so we don't get any Dom overhead which means no unnecessary element inside of this fragment let's open up a div tag and give it a class name of container a relative a flex a padding top of 20 a flex stash call just like that an item stash Center and a justify Dash Center and lastly on large devices a padding X of zero perfect let's open up this div give it a bit more space so you can see the entire class name in case you missed anything and then let's put another div in here with a class name of MX Auto a flex with of full a flex-all just 5- Center space y of six and on small devices we want to give this a fixed width so a w Dash and then in these angled array kind of brackets that you're used to from JavaScript arrays for a custom value we can put 350 pixels so a very exact value that we want it to be inside of here let's create one more div with a class name of Flex flex-all for vertical alignment an items Das Center a space y of two to space out the elements vertically and a text Das Center so the text will be in the middle um you know inside of here ghost or icons let's import that custom component logo so the little um hippo SVG icon with a class name of height 20 and a width of 20 okay before we continue in this page let's take a look at what we've already done right here it's always nicer to see what we're building in real time so because we created this folder called sign up again the off doesn't have any meaning for the URL because it's in parenthesis it doesn't mean anything it's just for or organization we should be able to navigate to slash sign that Dash up exactly what we named this folder and the contents of the page. TSX will show up and that is precisely it it's the hippo icon and below this we can add the entire sign in or sign up form both are going to be very similar in the end with some very um small feature um you know differences right below the icon let's put an age one a large heading with a class name of text 2XL and a font Das Bolt inside of this H1 we are going to say create an account right here awesome let's save that see what happens there it is create an account beautiful however what happens when people click on this page but already have an account they want to sign in and not sign up so therefore let's include a next justest link component with an H that leads to slash sign in inside of this link component let's say already have an account and then question mark sign in so we can offer them the option to just switch to the other page and this link is going to get a class name of or trusted button variants to make it look good and let's give it a variant just like that of link and see what happens let's hit save already have an account sign in beautiful and let's quickly overwrite the text color though with a custom class name and say text- muted D foreground and you know what actually it looks pretty good without this let's leave it as that the blue one I really like it actually okay uh that's I I mean I didn't prepare for that but I like this more actually it looks better okay awesome and below this sign in text let's include an arrow right icon still in the link component this is going to be self closing and let's give it a class name of height four and width of four you know just a little um you know Arrow right there pointing to the right hand side and also let's give this link a a um Gap property so the arrow has a bit more space so as the custom class name let's give it a gap of 1.5 for example so there's a bit of spacing between the sign in and the arrow perfect after the closing div after the link component let's get started in creating our actual sign up form and this is going to be a really cool part of the build I really mean it because you're not only going to learn how the full authentication flow Works how to securely log users in sign them up and send verification emails but also you're going to learn how to properly and I mean properly do forms in react a very very valuable skill that you're going to use in almost every project so many projects have forms and it's just a really good skill to have anyways at the top level div let's apply a class name of grid and a gap of six inside of this div let's create a form element with no action we don't want that with an on submit that we're going to Define in a second we can just leave it as some curly braces for now we're going to do that here in a minute and inside of the form let's create one more div with a class name of grit in a gap of two and inside of here one more div with a class name of grid gap-1 and p a p-2 a padding on the Y AIS of two awesome now inside of this div we're going to create two things an input and a label for that input that lets users know what it is and our UI Library ui. chat.com actually provides a beautiful input out of the box and also the label that goes with it for example I mean it's not going to be the checkbox right here but the label is going to be the same so let's install these components because they're going to look really really nice and by the way we're getting a little arrow here because we still need to mark this as a client side page because we need client side interactivity in it and you know the drill by now we do that by adding this use client directive at the very top of the file awesome let's hit save on that of course we still get a syntax error but anyways let's install npx shared cn- UI at latest and then let's install the input hit enter that's going to install the input component I don't even think it has any other dependencies as far as I know it's just pure HTML and Tailwind or jsx inwind unknown command input let's say add input I believe it was or was it install I think it was add let's see if that works there we go we've got the input and now also add the label CU they go really really well together and that's done too beautiful that means we can already get started in using this in our component let's start with the label component right here that we get from the just installed component SL label and this is going to be the HTML for the email input that we're going to create here in a second and this is going to say email inside of the label now right below the label let's open up the input that we got also from the installation just now this is going to be self closing as inputs always are and get a class name there we go off and we are going to apply a conditional class name so let's do this in curly braces with our CN helper function inside of here let's pass an object and we're going to apply the class name of focus D visible and we're going to apply if this pseudo selector is true a ring red of 500 now we only want to apply this to highlight the input in red if we have an error in the input we haven't implemented that just yet so let's leave it as true just for a second so we can continue and don't get any syntax error and then we're going to tweak that once we actually implemented the form logic behind this as the placeholder in this input placeholder we can simply say um U at example.com for example because this is the email field and we are going to leave it at that just for now let's copy down the div containing both the label and the input so the grid Gap one padding Y 2 one copy that down using shift alt and arrow down and that is going to be our password field so instead of the email let's put password for the HTML 4 and also password inside of the label and the placeholder you know can be anything let's just put password for example right in here and let's get rid of the on submit actually just for a second so we can get rid of the syntax error and actually see what we are building right here on the page so let's start back up the development server close out of the console and then the develop server is going to show up right here on the right hand side so let's see what we're building let's refresh the page and we should be able to see right here an email field and right here the password field right below it both contained in a form and let's restart this and see what this looks like and there we have it we've got the email field we've got the password field and right now of course we're mocking the arrow state so it's going to be red that's what the conditional class name is for if there is a problem in the field for example your email is invalid that's how we make a beautiful Arrow State for the input field perfect and now all that's left is a big ass button that lets users actually log in you know so with one closing div and then the closing form let's begin above that and simply use or trusted UI button right here that's going to say sign up there we go save that and see what happens or button should show up right here on the right hand side there it is a big ass sign up button that users can click after they enter enter their email and their password very very nice and now we need to get to the actual form logic you you know can enter anything here click the button literally nothing happens other than the standard behavior of the form getting submitted and that's not really what we want so Josh how do we properly handle forms in react because honestly that is an art in itself and the answer is there are some libraries that make it really really easy to do proper form handling so let's quickly navigate into our CMD and say yarn ad and then there's a couple things that we want to add for example the first one is react D hook- form if you're not familiar with it we're going to learn it together stepbystep don't worry let's add a package called at hook form SL resolvers that's going to make it very easy to validate the fields for example to see later if the email is correct or the password is at least eight characters and what not whatever we specify that package is going to make it very easy we want to install Zod as a schema validation Library it's going to do pretty much the same thing you're going to see what it looks like let's install zoner this is a toast notification Library it has nothing to do with a form actually but if there is something wrong with the form we can display like a little a little notification a toast message is what it's called at the very top of the screen for example to let the user know that they have been logged in successfully or that something didn't work that's what that is useful for and let's hit enter on that so that's going to install a couple of dependencies and did I forget anything what happened your project contains log files generated by tools other than yarn so there is probably an npm package lock right here yep there is I don't know why actually probably still from the next J initial initialization we can also get rid of the yarn arrow. log and let's try this again let's run yarn so everything resolves perfect and let's try yarn ad these dependencies now and hopefully nope and turns out I did a little typo it's not at hook for/ resolver but it's resolve vers in the plural and then that's said enter and hopefully that's going to install all the dependencies just perfect it looks like it very nice let's let that install for a second and then we can restart back up the development server close goes out of our console cuz we won't need it anymore and let's get started in learning how to properly handle forms so the first thing is we're going to destructure some things let's go in our page and say con empty object for destructuring is going to be equal to use form now um the Auto Imports don't seem to be working again so let's quickly reload the window and see if we can now import the use form because ideally this should come from our react hook form let's see if that loads now if not we have to manually import it and it still doesn't work okay whatever let's go ahead into the Imports and say import use form from react hook form there we go a very useful hook as you're going to learn right now so what does this use form allow us to do essentially we can destructure three things from it for example the register function and you know how usually in react you have to say um for example for a controlled input you have to give it a value and then also a onchange Handler that sets that value as state well this is abstracted away from us for Simplicity by react hook form with this register function let's also destructure the handle submit and lastly the form State and the only thing in the form state that we care about is the errors so we can later display them and for example make the inputs red based or where was it right here based on if there is an error or not so we're going to destructure the register handle submit and the form set from the use form and inside of this use form this actually takes an object and we can pass it the resolver now the resolver is a custom schema validation resolver that we can pass for example our Zod resolver the schema validation library that we are about to use this Zod resolver let's say import Zod resolver comes from at hook for/ resolvers SL Zod just like that and this obviously expects a schema validation U you know a well it expects a schema that we can use for validation now what the hell is a schema let me show you let me demonstrate what a schema is let's quickly import Z from Zod if you don't know what this is don't worry I'm going to show you right now so for example a schema let's say const and then of credentials validator is going to be equal to a z doob now a z. object is pretty similar to a regular JavaScript object in that we can simply give it properties and you know those are then going to be object properties for example for the authentication that we are creating right now we expect an email field this email field is going to be a z do string so it can't be a number it can't be an object or an array it has to be a string and this string can be a email this is simply a utility that Zod provides to us that checks if there's like an add sign in the email and a do Com or dot whatever you know just so the format matches in email that's just a little utility Helper and we can also Define the password what should that be like for example The password should also be a z. string and we can enforce all the logic that we want of course these need to be common separated what what do I mean by the logic for example this has to be at minimum eight characters and if it's not then we're going to display an arrrow message for example the message is going to be password must be at least eight characters long and this message will get displayed beautifully later in our form right below the input that is wrong actually so it will be in red letters right below the password that is the beauty of zot our schema validation Library so if we put the O credentials validator into the Zord resolver now you're going to notice that all the arrow are all of a sudden gone it just works when we pass it right here and we can also that's a beauty of Zod as well get the actual typescript type directly from this for example the type let's call it t type of CR or credentials validator there we go is going to be equal to Z Dot and then infer we can simply infer the typescript type from this schema we can pass it the type of or credentials validator from above and that's a valid typescript type that we can now use inside of our use form we can simply pass it in as a generic just like this in these angled brackets and what that means is that this register function is now fully typ saave and so is the handling of the form for example what does that mean I know that sounds a bit abstract in this input right here what we can now do is go ahead and say in angle or in these curly braces dot dot dot register so we're spreading in the return value of the register function and in strings we now get type safety email or password where do these come from right here the email and password that we defined in the credentials validator so the email field of course should be registered as the email and just like this we get the state for example we don't need an on change we don't need a value this is what is handled from the register and the input for the password very similarly we can say dot dot dot register and register this input as the password field now if that's not beautiful I don't know what is and the cool thing is now we get access to the arrrow values for example instead of always making these red let's say the errors Dot and then email if there should be something wrong with the email that we know is wrong when it doesn't match what we defined up here or something wrong with the password if it's for example only six characters and user hits sign up then something is going to be wrong as we defined then the field will turn red for example here the errors. password then we want this field to turn red and that is really really cool and as for the form on submit that we can now successfully do let's say on submit just like that we can pass it the hand Le submit from react hook form and then any other function that handles this submit for now let's just call it onsubmit a function that we are going to create that handles the submission action of this form containing the email and the password let's say cons onsubmit is going to oops onsubmit is going to be equal to an arrow function and we already know the type of the data that we're going to receive in here and that is going to be let's say uh you know empty object and this is going to be of type t o credentials validator right here because react hook form through the through the handle submit make sure that the data passed into our custom on submit actually matches our schema for example we can now destructure the email and the password right away because we know the type and work with it in or onsubmit that is insanely like it that is an insane developer experience it just works um like that and that is beautiful now one good practice I encourage you to follow along with is not declaring the validator right here in our page but actually in a separate file so that we can reuse it later on the back end because on the server side we can use the exact same thing right here the exact same schema to make sure server site that the incoming data is actually legit and we want to handle the user request and to do that let's go into our lib folder create a new folder called valid Lors and inside of this folder let's create a file called for example account credentials credentials validator dots and paste our contents right in here of course we still need to get the import right and we need to export both the actual schema from Zod and the type so we can then import these in our page and later on the server so with one logic right here in this file we can handle both the client side validation and then the server side validation later when it gets to the server side um sign up part you know when it gets to that logic and the only thing left to do for us now right here in the onsubmit is to actually send this data to the server to actually handle the signup logic see if there is an account with this email if there is well then you know they should be redirected to the sign in instead of sign up because they obviously already have an account and if there isn't account then we are going to send them the verification email so this entire flow right here we just did this part the sign up page with email and password this is done and now we move to the server side so this is client and all this right here will happen on the server so we can make sure it's secure whereas the client you know you can never trust client input they could basically send any data to your API we did this part now we're going to move on to this part the verification email that happens on the server now we could just get started right away and handle all this in Express but there is one Library called trpc trpc doio that you might know you might not know it well now what does it do as a very brief summary it allows us to maintain type safety through front end and back end through both which is insane right when we change some data on the back end we get typescript errors on the front end if we you know change the data where it would break or front end or and the other way around it's an insanely useful tool and now the only kind of bad thing I personally find about it is the setup the setup will take like 5 to 10 minutes it's you know not not the best part I'm going to be real with you but you're going to see that the outcome is completely worth it I literally use trpc for every single project that I possibly can um and I couldn't imagine working without it it's so useful and I genuinely mean it so let's get the setup behind us let's say yarn add then add trpc client at trpc SL next so we can easily use it with nextjs at trpc react dqu and at trpcs server it's going to handle the server site and one more thing that's going to be at tanack SL react dqu and hit enter on all of those I know that's a lot of dependencies as I said the installation is not the most fun part but then actually writing the apis is going to be nothing short of you know incredible because it is a very very nice tool has like 30,000 Stars which is pretty crazy to think about but it is that good honestly okay anyways let's stop uh you know talking about how good it is and actually explore it for ourself the first thing we're going to do is navigate into our components folder and create a new file called providers. TSX and this needs to be a a client component use client at the very top of the file very important because it will Define a lot of client side behavior let's say cons providers is going to be equal to an arrow function and we can export that as the default export default providers at the very bottom of the file just like that okay this provider's function is going to be the it's going to allow us to use trpc throughout the entire frontend part and to achieve that let's say con and then in angled brackets query client is going to be equal to use state so that's why we turn this into a client side component and this use St takes a callback function that just returns a new query client so we are instantiating a new class and the Auto Imports don't seem to be working because it should suggest us to import this right here from tany react query and it won't so let's import it ourself let's say import and then uppercase q query client from at tanack react D query and instantiate that class right here in this UST State callback okay that's going to allow us to use react query and trpc the API tool is just a very thin wrapper around react query so let's say cons same syntax trpc client is going to be equal to use State this again will take a callback function right here and return something called trpc doc create client and not trpc client but Simply trpc Now Josh where the hell does this trpc come from it literally doesn't exist in our app and don't worry I know it doesn't so let's go ahead and create it and return back to this component here in just a second so let's go back into our source directory and in here create a new folder called trpc This is going to contain everything related to this library for example or initialization of this trpc client right here let's create a new file in here called client. because that's all it is or trpc client and literally the only thing we're going to do in this file is export cons trpc is going to be equal to a create trpc react there's a specific integration with react and we need to import this now the Auto Imports again don't seem to work I I don't know man might be having a bad day who knows let's import create trpc react or self them from and this comes from at trpc react D query so the you know react query integration which after all again is just a very thin type safe wrapper around this takes an empty object we don't need to pass it anything custom and it also takes a generic now the generic goes into these angled brackets and this generic will contain the entirety of our back end and if you think that sounds crazy you know maybe it does but really it's not let me show you what that means so in our trpc folder let's create a new file called index.ts and essentially this is our back end it might not seem like it right now you know but you're going to um see for yourself what this means let's export a con called app router from here and this is going to be equal to a router and this router is nothing more than a utility that lets us Define custom types saave API endpoints and now I know we're opening up a lot of files unfinished files at the moment that's fine it's all going to resolve very very quickly and let's open up one more file that is not resolved yet and let's call it trpc dots and this trpc will serve the purpose of giving giving us this router that lets us Define all these um API endpoints or entire backend so to say and in here we're going to say const t for trpc you can call this anything but let's just call it t it's going to be equal to init trpc something we can import from trpcs server do context and we can invoke this and call the dot create right on here and now for the router that is nothing else then let's say export con router is going to be equal to t. router wow surprise surprise and by the way let's not invoke that it's just the T do router just like that we can save this file and while we're here let's also create a public procedure let's say export cons public procedure now what does that mean essentially it just means that anyone will be able to call this API endpoint it's a public endpoint there's also private endpoints we're going to get to that later for protected access for products that you bought for example not everybody should be able to do that but the public procedure is for end points that everybody should be able to call and this is a t. procedure now let's insert a you know just a little space here and now we can actually import this router right here in our index.ts this takes an object and we could leave it as it is and now we can actually Define any API route just like this for example this is going to be a public procedure do query and the only thing by the way this is just an example we're going to remove this later again just so you see what this does let's return a string in here saying hello and this is actually an API endpoint we just created this is our server in a fully types safe way so let's revisit the um client for example remember the generic we want to pass into the create trpc react where does this come from and the answer is right here it's the type of the ab router or entire back end so we can simply export the type app router and this is just going to be the type of app router no black magic no super special trpc stuff just a very simple type off that we can now pass in to this helper so let's say app router right in here and we need to import this type and for some reason doesn't work so let's say import type app router from and then do/ uh index or I think we don't even have to put that just from do slash for example and just like that the front end now knows the type of the backend no actual data is transferred just the type of the ab router which will get removed at build time anyways by the way so this is just for us during development and this is how we achieve a full stack type safety between front and back end beautiful let's revisit these providers because now we can call this trpc or we can import it and actually make this whole thing accessible on our front end this takes an object a configuration object that we can pass for example the links property this takes an array and in here we're going to say HTTP batch link what that's going to do is let us batch requests together for m maximum performance later this also takes a configuration object and no um you know c in there just like this this gets a URL and this is going to be let's put a template string so we can interpolate a value or process. env. nextore serverurl and then slash API SL trpc so this is the URL where this back end that that we just created right here will be called later so for example we can call the any API rod on this URL right here later it's also pass it a fetch function right here that gets a URL and some options and the only thing we're going to change about this is let's say right here we're going to return the exact same thing so a fetch of the same oops a fetch of the same URL with all the same options so we can spread them in the options right here the only thing we want to change about this is that we want to change the credentials to include just like this so this works properly through Express and through nextjs both at the end so for example all the cookies will be passed as well beautiful and we're almost done with this provider's component the only thing left to do right now is return the actual providers like the literal context providers let's return for example the trpc do provider and this takes in a client and this is simply going to be or trpc client and also a query client which is going to be our query client we defined above for both inside of here let's put a query client provider and this comes from tansy react query this has literally nothing to do with trpc the only thing this allows us to do is to use 10stack react query completely independently of trp see if we wanted to because that might happen in the client this takes let's put the query client as well it's the same thing so again TPC right here just a very thin type saave wrapper around react query that we could also use as a standalone Library the latter and inside of here let's put the children so we're just going to render out everything else in the Dom um just provide the context to it of course these children we need to accept them right here and in the providers let's say children destructure them right away and there's a really cool react type we can use for the props that is props with children that's literally a regular interface just already with a children defined if you wanted to do this yourself essentially what this does it's the children of type react node it's the exact same thing just a more concise way of doing that and just like that all the important work of setting up trpc is already done we can now go ahead and go into our main layout. TSX and simply provide these functionalities on the front end to all our components and we make them accessible by simply wrapping our entire main content right here in these providers so let's say providers right here nothing we need to pass as props it just works let's wrap the entire app right here inside of these providers and that way we make trpc and react query accessible to all components that live right here here under these providers for example these would not have access cuz they're not wrapped in the providers but this div and all the Navar content and everything that's inside of those will have access to trpc okay little cut there because for me it's the next day actually so I just noticed we have an error um but let's get started anyways it's like 8:00 a.m. so excuse my voice sounds um a bit the rest because I woke up like half an hour ago but I have my coffee here and I'm ready to get all started in this project with you okay so first up about this error that we've received right here I don't think we've seen that in the last session at all and it's really weird that we get it but there is a very clear reason as to why so take a look at the package.json and it's because of the version of react query cuz about one or two weeks ago they released the 5.0 version which is actually not compatible with the most recent release of um trpc for next year the little you know you utility we use to use trpc with next and to get around that we can simply install a different version so let's jump into our command line and say yarn add and then add 10stack SL query and I think it's react D query right here and let's check the version of the repository and this is four oops I didn't mean to do that 4.36 point1 this is the one we're going to install because again the newest version of trpc and next are not compatible with this 5.0 there are experimental versions of trpc next you can use that works I tried it I don't think it makes sense for this project there were errors and it was not worth the effort when the old one works really really well so let's go ahead and install the 4. 36.1 just like in the um repository that I'm going to provide to you right here on the right hand side the digital hippo and what did I do wrong cuz we need to say at 4. 36.1 let's let that install let's going install this very specific version of react query and maybe in the future I will even do a little adjustment on a separate branch on this report I'm not sure about this um but when they support this in like a month uh two 3 months from now I don't know when but officially anyways then we might do a little update to this repo but the changes are not big and there's absolutely nothing wrong with using D4 version because after all I mean we're also using the absolutely newest NEX S14 version and there's absolutely nothing wrong with going one version down for react query cu the changes are very very little I did the entire integration for 5.0 and it was not a huge difference anyways let's start back up the development server and go into our local host and then see if the error is gone which it should be so let's restart this page we can close out of the um entire console let's restart it and see what happens so now we shouldn't get any error perfect and there is no error so that's the perfect bases to Now set up um trpc in our server. because there's one or two lines of code we need to add in here to essentially when we get a request in or server because we're self hosting we will get it here we can simply forward that to trpc in nextg to handle it appropriately and there's a very convenient very easy middleware we can simply use for that so for example let's say app.use here in or server. TS and in here let's say under SL API trpc when we get a request to this endpoint we want to forward it to trpc in nextjs and the way we do that is by calling something that is called trpc trpc Express right here and this comes as an import let's import that at the very top import Star as trpc Express from and this comes from at trpcs server SL adapters oops adapters and then SL Express just like this so we have an Express adapter there's a bunch of other adapters for you know other Frameworks you want to use as we are with Express and on this trpc Express there's only one thing we can call that is the create Express middleware which takes a configuration object inside of here we can pass the router which is nothing else than or app router from do/ trpc and it also takes something called create context now this create context is going to be important because it allows us to Simply take something from Express like the request and response for example that we get in Express and then attach them to something called the context to be able to use them also in nextjs like imagine we get them here and then we also forward them along with the request to or API endpoint and the way we can do that is pretty simple we can simply Define a function where we um Define this create context right here in Express so at the very top of the file below the port let's say cons create context and this is going to be equal to a you know an arrow function that we are going to destructure something from and this is going to return an object immediately by the way just the fact that this is valid syntax in JavaScript is kind of weird anyways what what we can do inside of this create context is destructure the request and response and we also need to tell typescript what type this is and this is going to be a trpc express. create Express context options type that we can simply call on that trpc Express inside of the destructuring we got the request and response and we now simply want to return the request and response as they are without any changes right here from the context and that means we are essentially making them available in our nextjs API endpoint wherever we handle these API calls from trpc and that's all the work we have to do on the back end or site works again after saving this page the eror is gone and the last thing we need to do is you know we are never actually receiving this request anywhere we are now forwarding it to next CH so we get it on the server let's quickly jump into our drawing tool right here excal draw because as I mentioned I want you to understand why we're doing what we're doing and why it works so this was the authentication flow now let's quickly take a look at the request flow so whenever a request comes in to our server right what happens first off it goes to the express server this handles everything like the rendering the requests and whatnot and when we say right here in or server. Ts that we want to forward a request from or whichever Quest goes to this endpoint in our server we want to forward it and handle it using this middleware right here what essentially we are doing is taking this request right here and then sending it over to a certain API endpoint that lives in nextjs land right here so this is next J oops nextjs where we can now handle this request using trpc for example and the way we do this is we need to listen inside of nextjs to this call that is forwarded and we do that inside of a route Handler so let's say route Handler and this is nothing more than an API endpoint inside of next Jaz that kind of listens to this request coming from Express so that allows us to then handle it accordingly however we want you know and that's all the logic there pretty much is to it um so let's quickly do that because it's really easy so let's move that into a side by side go back into our app and the way we do this is using a endpoint so we need to kind of match this pattern right here under / API trpc and then whatever that's going to be some Dynamic trpc methods passed in there automatically we don't need to worry about it so this is going to live in our app folder let's create a new folder in here called API inside of this API folder that we just created again we need to match this pattern right here so it's going to be trpc next so let's create a folder called trpc inside of this trpc folder one last folder and that has some angled brackets and this says trpc as well in the angled brackets now these angled brackets we put the trpc in why did we do that because this is now a dynamic route because of this syntax otherwise it wouldn't be now what does that mean a dynamic route essentially if we had something like SL API right here API SL trpc SL trpc both are hardcoded and then slash anything right there's no way to statically know what this anything will be at runtime so whenever users are making requests and therefore if we take turn this into a dynamic route we always get access to whatever comes after SL trpc slash whatever whatever we get access to this whatever whatever right here under the trp well we can now access it in the page essentially by turning this into a dynamic route so because there's no way to know what that's going to be statically we can turn this into a dynamic route with a page. TSX inside of it so we now get access to whatever comes next and as I mentioned earlier this is going to be something trpc internal we don't need to worry about it that is essentially how trpc handles its remote procedure calls to make the lookup on the back end for the method that we are passing or that's automatically passed in here we don't even need to worry about that inside of this trpc page. TSX or no this should be a route. TS and not a page. TSX very important we need to name this route. TS this name is in forced by nextjs so we're actually to able to handle um API requests so it has to be named route. TS inside of here we need to now handle this trpc request and essentially we can just let trpc handle it so let's say const Handler inside of here is going to be a function that receives the nextjs request this is going to be of type reest this oops reest there we go this is a standard web request inside of the aror function with that we are receiving right here so there's nothing we need to import in terms of typescript types it's already built in and then we can simply now handle this request for example using the fetch request Handler that trpc provides to us automatically so we can simply go ahead and import the fetch oops fetch request Handler from oops from and then at trpcs server adapters and let's close out of this so we have a a bit more space slash fetch so whatever the request is we can simply pass it onto this fetch request Handler to do all the heavy lifting for us and that's really cool inside of here this takes an endpoint and this endpoint is of course going to be or/ API trpc endpoint where this lives the request we can simply pass it right in here for it to know what it should do then let's pass in the router that should handle this request and that is or app router essentially as I mentioned earlier this is literally our back end and then lastly this takes a create context and this can simply be an empty Arrow function just like this and now we can export at the very bottom oops export because right now this wouldn't do anything if we didn't export it nextjs would not know how to handle this request even if it got to this file so we need to export the Handler as get so now it handles get requests but we also wanted to handle post request so we can say Handler as post as well so now both for post and for get requests to this specific API endpoint right here this file is now going to handle them and this function right here do does all have a Lifting for us so we don't need to worry about it and that's already it well already you know as I mentioned the setup is by far the or my least favorite part of course um but I think we got it done and now it's time to try out if everything works correctly so let's head over to our sign up page let's close out of all of this for now and try if this works so let's go over to or o and then sign up page. TSX and let's see if this works so for example the procedure we want in here is a sign up procedure where essentially we want to create a user once they enter their credentials and they are valid the way we can do that is by say const and we can initialize an m object to worry about the destructuring later and say trpc let's import that and if we now say dot we can see all the API rods that are available to us in our case that's going to be the mocked any API rout that we already created and we can simply use the use Query on that all in a completely types safe way and if we now destructure the data from here we should see that this is either a string or undefined because if we control click on this any API rod because um right here we are returning a string if this was a number and this is the beauty of trpc understand this if we return a number here and then hover over the data again in real time we get type updates of number or undefined if you're wondering why undefined well because this is a client side fetch so when the page loads it will be undefined for a very short time while the trpc call loads the data and fetches it from the back end and then it's going to be popular at with our actual data which is going to be the number that is huge at work we use something very similar that we developed ourself however it uses code gen with this trpc there is no code gen needed at all so now for the most fun part let's log out the data and see if we actually get the data on the front end so let's head over to our sign up page SL sign Dash up and see what happens let's open up the console right here and or server crashed or what oh no it restarted because we changed the file okay that's totally cool that's um by the way we can get rid of all the unused Imports like Zod for example by pressing shift alt and O that's going to get rid of all of the unused Imports very very handy and then let's let our server restart and then try out oops did I switch to the English keyboard again I did and then let's try out if the oops I didn't mean that um try out if the tier PC works so let's close out of this go into the console and beautiful we can see the hello right here in the console so now we know that the trpc request Works successfully and we can actually get started with all the authentication logic um which is going to be a really really cool part of this build so first off we can get rid of the any API route we don't need that and instead we are going to create a completely new router and this is going to be a off router so if I say that this is our back end that's true but it does not mean that we literally need to put all the API logic inside of this file that's not true so what we can do is something like this for all o procedures we want to use an O router and the off router is literally the exact same thing as an AB router so we can just do the same thing for example instead of trpc we can create a new file um right here let's create a new file called or- router. TX yes and this is like a separate API endpoint you can imagine that handles all the authentication logic let's export a const o router let me show you how this works and this is nothing else than the same router we get from TPC just like the app router there's nothing special about the app router it's the same thing as the off router now on this off router we need to create a user for a sign up page to work so let's call it create payload user because it will essentially do just that it will create a user inside of or CMS and that's all it does this is going to be something called a public procedure which just means that literally anyone can call this API endpoint and they don't need to be logged in to do so later on we're also going to define a private procedure where only logged in users can call it but of course the sign up endpoint should be public because you won't have an account when you call it forcing a sign up or a sign in state there doesn't make any sense now we expect an input on this endpoint and what do we need well we need the email and the password and we get those from or o credentials validator that we have written which just means that the email and password will be passed along from the U form right here we created on the front end now to our back end and this is the shape of the input that we expect here if we don't get this input in the API endpoint it's going to throw an error and as for the actual business logic that's going to be handled in the mutation inside of a callback function just like this right here so what are we going to do in here first off we need access to the email and the password that the user sent along and we can do that by destructuring the input from our mutation so let's say con mty object is equal to input just like this and now we can destructure the email and a password because we know we will get them by using this off credentials validator in our inp put right here awesome now we need access to our CMS to be able to create a user so let's say con payload is equal to a wet await get payload client right here and we also need to mark this mutation as asynchronous right here in front of the Callback function to be able to use a weight inside of it okay now first thing we want to do is check if a user is already there so let's say check if user already exists because if they do they can't be signed up again we don't want to create an account for somebody that already exists so the const and then docs that we are going to D structure which is short for documents is going to come from is going to be equal to await payload Dot and why are we not getting type safety here we definitely should okay payload is any why is that oh because we didn't set the promise so the get payload client currently typescript doesn't really know what it will return but we can tell typescript what it will return by using a you know type annotation right here so after the arrow function a colon and then we can simply say promise and this is going to promise a payload instance and the type we get from payload just like this so now we're saying hey when this is done running when we await this operation then we're going to get a payload client back and that allows us to get full type safety wherever we use it like in our um orth router for example now to get data because now we want to find find a user if they already exists we can simply use the find method in payload that takes an object and as the collection we want to find some somebody from the users collection now we haven't created that we're going to do that in a second and now where do we want to find the user cuz this is literally just going to give us all users in our entire application we can apply a wear filter and this is an object and we want to say where the email of the user and then another object now we have a bunch of methods we can call for example you know is like in greater than equal whatever whatever we just care about where it's exactly equal to the email that is passed into our API endpoint so essentially what we're saying is give me the user where the email matches the email that is um coming from the input right here that is sent along from this field right here and we can give these docs a custom name for example let's call them users to be a bit more specific in our code and now let's do some logic checking for example if the users. length is not equal to zero that means this operation actually found a user which it shouldn't because we're trying to sign up a new user in that case let's throw a new trpc error it's a very nice utility we get from trpc that makes error handling a lot easier this takes an object and as for the code we can simply say conflict because there is already a user with these examp credentials let's save that and go on with our actual operation if this did not trigger then that means there is no user with the current email and this user is actually totally cool to create Now to create a new user we can say await payload do create very intuitive and in here we can simply add the user to the collection of users now a collection is nothing more than like a database table the name is a bit different but imagine like a database table like the users table that we now want to add something to that we want to create a new row in essentially that's all it does and as for the data that we need to create a user well there's nothing really here there's some default values that payload gives us because the users's table or collection is always there it is automatically generated for us but that doesn't mean that it's really good so we want a custom users table a custom users collection and the way we can do that is very straightforward let's go into our source folder and then let's create a new folder in here called collections which is essentially a going to be a database schema in that sense and here let's create a new file and call it users. TS just like that now what is a collection I know I kind of explained it earlier but let's write one together and I think you will really get the hang of it so essentially let's say cons users and just for typescript so that is happy and gives us actually intelligent so we know what we can type in is going to be of type collection config that we get from at or not at payo just payload SL types and this is going to be equal to an object now if we hit control space we can see there's a lot of stuff that we can pass in here if we want to but we don't have to lot of this is optional for example the stuff that we have to pass is a slug very important so let's um say as the slug this is usually just the same as the collection name in lower case so in orow case that's going to be simply users and then we can pass this some Fields now these fields are in Array and each field is basically like one entry in a database row so what does that mean for example let's create an object in here and this always takes you know some properties again but first off we want to pass the name and the name of this field is going to be the role so in or app we have two kinds of users let's quickly jump into our drawing tool types of us users okay so what do we have first off we have admins this is going to be you you are going to be verifying the product files for example you're going to be paying out your your sellers on your platform after you know the month is over after the return policy is over whatever um you know you prefer there's going to be admins that's going to be you and there's also going to be regular users now these users can be either sellers or buyers so the dashboard is very flexible in how we display the data for example a user can be a seller that means they have products listed on your app and maybe even sold a few of them and then there's also going to be buyers just like this buyers and both are going to be of type user so these right here are going to be the main types we are going to be working with in our dashboard and then this is just more for us and what is going to be displayed in the dashboard later for example buyers will be able ble to see all their orders in the dashboard and sellers will be able to see all their products in the dashboard and their orders and whatnot so there's going to be some adjustments on the dashboard as to what kind of user is currently logged in that's really really cool and it's not hard to do either and let me show you why it's not hard to do so let's scroll out a bit so we have a quick overview right here types of users and let's get started in defining this role field right here the type is going to be a select just like this and because this is a select we now also need to pass some options to this the options are in Array of objects and as the first object we could say for example the label is going to be admin and then the value is going to be in lowercase admin as well so this is what the user will read in the dashboard and this is the underlying value we assigned to that label that doesn't have to be readable so we're going to put it in lower case the user is not going to see this this is for us internally and the second option is going to be as you can probably guess we have admins and users so the second option is going to be a user and the value is going to be of course the same again user in lowercase just like this now if we saved this users's collection we still wouldn't be able to see it in the admin dashboard just yet let's try it out let's go to Local Host 3 3000 cell over to our admin dashboard let's log in and in here we can see the users table right here but if we click on a user and we see ourself right here well there's the email field where we can change the password and force and unlock if we try to you know log in too many times and got restricted but there is no select field that we just added and the way we can actually make this show up in the admin dashboard is very simple now we have to find the collection so we can go to our payload doc config.txt collections array from before well we can simply insert it right here into that collection array and if vs code loaded then we could actually get the Auto Imports working which we don't oh but we also forget forgot to export this um collection users so let's add an export statement right here and try to import it in our payload config and there it is we now have it shown up and can import it inside of the config now that's automatically going to trigger a restart on our server because we changed a file let's quickly open up the command line and let's manually restart this server because apparently it didn't trigger a but we probably don't have this um payload config in or nmon config so it won't watch this file that's fine and okay now we get a um error that collection slug is already in use users and that is a problem we can easily get around by by just telling the admin right here what collection it should use as the you know users collection that's a special collection because it is the default it is already added by payload and the way we can get around this is by saying user is going to be users right here so essentially we're telling or CMS hey check this collection right here for everything off related and that's all we need to do we can restart our server saying yarn Dev and it seems like actually it did trigger an automatic reload right here I don't know okay and now we get a type error that is cannot find name author router so what happened here is or back and try to compile in its entirety and of course we still haven't imported the or router inside of our main index. MTS and typescript so let's save that and try this again and then hopefully the error will be gone let's even open up another um console and restart this so this is going to start our server back up and now we are getting at lib validators account credentials validator whatever whatever is not valid and where are we using this probably in the off router right here so one thing I've noticed at is that this back end is not good at taking relative Imports I think you can get around it by kind of configuring weback um so where you do that is right here in the weback bunder you can configure this and there is probably a pretty easy way to do this but I just found it easier to change the um absolute Imports to some relative Imports by just adding a dot dot um so actually you know getting the actual path to the credentials validator and we can just you know get around that that way and that's probably the same for the get payload so let's turn this into a relative import as well and then we should be good on the server so for some reason that hung up let's say yarn Dev instead of a new um terminal let's restart this and see if it works now that we have the you know relative Imports instead of the absolute ones like before and it does so now it starts beautiful and now we should be able to see because we added these users right here in our collections we should now actually see them show up this field right here inside of our admin dashboard so let's restart or admin dashboard and see what happens let's give it a bit more space loading and then once it's done loading we should be able to see that we can now choose between admins and users and why is this loading so long not found the request what okay that's very weird we get the error of the requested resource was not found I think this is something very minor and I messed up summer so give me one second I'm going to debug and then I'm going to be right back with the solution okay and it was actually a pretty simple fix so what we did wrong is let's check out the payload config we have our users collection added right here and what we forgot is that you know the users collection is special as I mentioned earlier it's the authentication collection essentially this is where all the authentication logic will happen because you know that's our users with email and password and whatever so one thing we need to change is setting the off to true in the users collection by default it is but we forgot to add that and for now we can also add some access policies that is something or CMS provides to us which later on will be super useful in ensuring that only the right people can see the right data in their admin dashboard there's four things we can pass in here mainly and that's the crud stuff so create read update delete whatever for now we can add for example a read of true so a function that returns always true which just means that anyone is able to read all the users just for now we will change that later of course um to be secure and then for now we can also add for example a a create and this is also for now always going to return true so anyone can create a user and by the way one thing I realized while debugging which is unrelated is that in our get payload we are returning the cash. client a you might not have made that typo I made that typo and that is why the loggers in the server. were not working and I just realized that while debugging and just by changing that a that typo at the end um this works again so let's save all of this and one thing I've also noticed while debuging is that we are using a process. env. next public server URL right here in our payload config however we are never actually using umv to be able to successfully resolve this um next public server URL from our EnV file so let's go into our payload config and say umv we can import that from import Dov from EnV just like that and then say do config and as the path we can pass into this config where simply going to say path. resolve and then the current directory name the underscore uncore dur name which is a standard note thing we don't need to import that or anything and then we're going to navigate up One Directory so dot dot slash and then into the EnV file just like that you can hit save let's restart or server and let's see what happens let's head over over to Local Host 3000l and there we are it works we can hit into the users collection let's click on a user and now we are able to see that there is a roll with an admin and a user beautiful okay so we got unstuck from that amazing we can now see the fields that we add to our users collection of course later on a normal user shouldn't be able to see this field with admin and user and there's actually a really cool utility we can use for that so let's navigate into the users um collection again and this is where we can Define who can see these fields and who can't for example this Ro field we only want users with an admin role um to be able to see this right so for example what we can do right here in the um role right here we can say Ad oops in a new line admin there we go and this is going to um allow us to Define some admin dashboard stuff for example the condition which just means should this be shown in the admin dashboard or not and for us we can for example return false from here which is totally going to hide this field from our admin dashboard Al together so the values are there in our database we can use them anywhere in our app to determine if the user is an admin or a regular user however that won't be visible in the admin dashboard we could also for example make this visible only to admin users where we can destructure the request from this function right here that's why it's a function by the way and then for for example return rec. user. roll is equal to admin now only admin users would be able to see this field however we don't really care let's just return false that's going to hide it all together from the admin dashboard and that's totally fine for us let's reload the page and there we go the field is completely gone however we don't really have an admin user as of now if we take a look at into our database refresh this we can see there is no admin user uh right here there is no admin field on the one user that we currently have so let's quickly reset our database and when we create our first user then we will actually get the option to choose if this should be a regular user or an admin and one thing that's very important that we need to do on this role field is to require it so we can set the required value to true so this field has to be defined always you're either a user or you're an admin and by default we can set the default value you should be a user and not an admin unless we explicitly say so right when we create a new user we don't want to tell or you know back end hey make this guy a user because if we forget that then all of a sudden they're in admin maybe no if we just create one by default that should always be a regular user so let's restart or admin dashboard and create our first user together of course that's going to be us that's going to be an admin user so we can have access to all the admin stuff that we're going to add later like for example verifying other people's products let's create our first user confirm password there we go and if we now head into the users collection oh and by the way we probably just for now want to remove the false condition so we can you know actually see this field um so let's remove that that's probably yeah automatically going to restart our server and then we should be able to set that field um right here in the create first user that we're always navigated to by the way when there is no user in the database so we always have to have at least one user that's going to be you know you probably as an admin um unless we Define other otherwise so let's restart this there we go we can now choose the admin or the user role of course for us that's going to be admin um so we can see all the fields that we hide for regular users beautiful now we can see the users collection the role is admin and if we click on this user that's or email because you know that's us perfect that works okay let's get back to our whole sign up authentication step and because this CMS is typescript first there's something really cool we can add to our package.json to make our life a lot easier and to actually get the types for the users for example and this script let's call it generate the uh colon types just like this and what it's going to do is generate all the typescript types based on our config based on the collections that we have so for example for the users that would be um the role field that should be added to the users and there's a Coden we can use for that and we are going to write the command right here this is going to be cross-v again to share in environment variables crossplatform or cross you know cross environment I guess you could say and as the environment payload uncore config uncore path just like we passed in the dev script right here this is going to be equal to Source payload doig dots and for the actual utility we're simply going to say payload and then gener oops generate colon types so payload is going to know what to do it's going to handle the heavy lifting for us can save this package and try out or generate types script which is going to put all the types script types where we defined in the payload config which is right here in the output file so inside of a payload D types. you can name this whatever you want CMS types just types doesn't really matter let's run our yarn generate typescript and see what happens let's let this run starting payload compiling TS types and done in 5 seconds perfect let's check it let's go to payload Das types. TS there we go and go to our users right here we can see all the properties that a user has for example the role that we just added in the select field which is either admin or user an ID email password right here and so on perfect the important things for us are the email and the password because that is very important for our signup flow because if you remember if we go back to the off router and by the way we can close out of so many of these files let's just said close others so this is the only one we have open perfect if you go back in here as you remember we wanted to create a new user and now we can with the username and the password so let's open up the data and we can see there's some stuff we need to pass now like the email the password and the role because we required this field to be set you know either admin or user and by default it's going to be user as we said it so we can simply pass in the email that we destructured from the input right up here same with a password we can simply pass it in and then as the role we're going to say user just like it is for default and then finally we can return from this trpc API return a success value of true so on the front end we know hey this worked and we also want if we go back in or and let's put this into a side by side just like this if you remember what we wanted to do on the sign up we want the sign up page the form to be submitted to our API and then send a verification email right here and because this operation was successful at this point and we created the user this operation right here the creation will in a second at least automatically trigger the signin email to be sent this verification email to be sent to the user so when we get to this point of the return we already know that the email was sent so we can simply return a send to email and set it to the email that was passed into this API rod as the email now this email functionality we're going to do here in a second let's first verify that the entire offlow Works to this point so let's save the off router go back to our off page and you know start the development server let's say y Dev and one thing we need to do with the credentials that we get in the onsubmit is of course pass them to our API route with trpc which we're not doing currently we can get rid of this mocked call up here let's say con's empty object to worry about the destruction later is going to be equal to trpc Dot and then because we have to find the off operation on our index router we now get the dot off and can call the do create payload user do use mutation which is nothing more than a you know method this use mutation to kind of make post requests anything that changes data deletions modifications whatever as long as it's not a read operation for a use Query we can use the use mutation just like this we can call the or we can get the structure the mutate function from here and also the is loading state so we can display some beautiful loading States here later the last thing that is to do now is to remove this mocked thing send data to the server and actually call the mutate with the email and the password that we get sent to in the onsubmit right here submit them to our API and let the back end handle the logic right here perfect let's go into a side by side I don't know why it keeps exiting the side by side go to our main local host then SL sign up just like that let's leave the console open in case anything errors and we can type in an email let's not make it or original email but let's make it for example another email that I have and anything as the password and by the way I just notice that this is clear text we probably don't want that so let's go down to our um password input right here and SD type let's say password just like that and that's going to turn the password into you know these kind of obious skate blobs instead of having it as clear text just like that and then let's maximize oops let's not minimize let's maximize the page and see what happens in the network tab um so let's head over to network right here let's click sign up we send over the um API request for back end to handle and we can see a success true and a sent to email of the email that we just entered in the sign up now what that means is that the sign up was successful and we should be able to see a new user in our database so let's take a look let's click browse collections here in mongodb or if you're following along in postgress and check your table there and we should be able to see perfect that it has create a new user in our database with the email the password was automatically done and sorted and hashed so we don't need to worry about anything security related there it all just works out of the box and because that's what the CMS does for us beautiful that just works okay now now the verification email right super important we don't want anyone to be able to create an account with pretty much any email right now I could use your email to sign up to this service to digital hippo and that's not what it should be like of course we want to make sure that user owns their email and to be able to send emails that don't go to spam let's go to resend. comom um this is not sponsored this is a tool I like for my um personal projects um because it makes you know sending emails very easy it has a pretty good free tier and the emails don't land in spam but they land in the actual inbox so it's a really nice tool with a great integration to or CMS um you could do this any way you want you could have a custom SMTP if you want I did that in the past that also works um but I think for this video unless you're super into all that Network stuff and sending emails and whatever um it's going to be much easier to follow along with resent awesome after logging in you're going to land in the dash dasboard and let's head over to API keys because that's all we need let's click create a new API key and by the way in case you missed it the um domain is rent.com um I suggest you follow along using it again you don't have to I think it's just a lot easier as the API key name let's enter you know digital hippo uh you know let's leave it at that digital hippo permission full access that's fine domains all domains that's fine as well and then let's hit add that's going to give us an API key we can copy so let's right away because we only ever see this once go into our umv file and by the way I changed the secret uh for debugging earlier doesn't matter it it didn't change anything um so whatever your payload secret is um it it doesn't matter as long as it's anything it should be kind of secure anyways let's declare or resend uncore API uncore key right in this en file and paste in our API key hit save and what this allows us to do basically we are done with resent we don't need to do anything else in their dashboard whatever we can purely use this programmatically in our code and to do that let's navigate over into our get payload this is the payload creation right where we create the CMS instance and we can go ahead in this file and directly pass in the email transporter and we are going to do that using a package Ynet called node mailer super popular node email package if we check it out on npm let's search note mailer and quickly give it a bit more space right here note ma note mailer you can set this up with any um you know SMTP you want I for client projects for my freelancing um like a year ago I always use custom like my own SMTP um now we're going to use recent has 3 million downloads a week it's a very popular package for sending emails um in nodejs so we can add that package not mailer and then import node mailer from node mailer and not form it should be from note mailer just like this and also it doesn't bring its own types so we need to install these types separately so let's quickly uh stop the development server again yarn add minus D4 a development dependency at types node mailer and also add that and that's just going to make typescript happy it's a pretty old package it it's a definition types right here it brings its own types um but not by default you have to install them them separately let's start back up our development server close out of this and now configuring the email with the entire verification step as here on the right hand side is super straightforward okay first off what we need to do is Define our transport so the cons transporter this is what's going to send our emails is going to be equal to not mailer do create transport right here this takes an object and we need to pass this for example a host this is going to be SMTP oops. rent.com assuming you're following along with resent which I recommend um if you're familiar with all the stuff this would be your custom host a secure value of true a port oops a port there we go of 465 this is a you know standard port for email that is secure and for the off we need to pass a user and a password in here now this would be your custom stuff if you followed along with your own method or in the case of resent the user is going to be resent and the pass is going to be process. env. resore aior key so we what we just declared in the um EnV file with our API key right here that is our transporter and just like that that's what we can use to send emails for example also the verification email when a user signs up to our service and now to actually configure that let's go down into the payload do init step and pass in the email right away it just takes an email that is an object and inside of this email right here we can pass the transport which is our transporter the from address would be anything you want basically for example for me that would be hello joshr coding.com please don't do that because you can't you have to verify your own domain if you want to use this and you can't verify my domain if you don't have a custom domain or a custom email that you can send from you can always use onboarding at resent I believe it's recent. deev right recent or rent.com rent.com use this email right here unless you want to you know do your own domain stuff this is always available use this and then the from name can be anything for example digital hippo okay now of course if you actually deploy this later you probably don't want this to be onboarding at Rec send.com but your own domain for now this is totally cool and allows you to just get started with sending emails awesome and that's already all we need to do now let's go into our users collection and simply enable this feature for for the verification emails because off can not only be true but also an object where we can set for example the verify property right here and we could also set this to true you know that would work or we can set this to an object again and specify the generate email HTML because we can completely send custom emails right here this is a function that takes a token and this token we will use to verify the email later and now we need to actually return the email for example let's return a string that is a P tag saying hello please verify you know just for now to make sure that the emails are working let's save that and now we should be able to delete the user in our database sign up as them again and now receive an email this time for example let's get rid of this user right here oh why did that not work let's give it a bit more space click delete right here and then the user will be gone awesome now let's try this let's is the development server started yes it is let's go to our sign up page let's refresh just in case and then try signing up as the same user again and this time we should get an email that literally has the content of hello please verify okay it's paste in the email again enter anything one 12 three as the password hit sign up that should send the email and let's see in the input box if we receive that email I've got it open here on my right hand side my email client and we will see what happens oh you know what because we changed the back end we might have to restart our server all together let's say yarn Dev restart our server because we made some changes in the back end which I don't think we are watching in noon so it probably didn't restart automatically now to reflect those changes let's restart the server and try this again we can close out of that oh and I just just notice it puts the email and password in our URL bar that's not something we want we're going to fix that here in a second however let's first make sure that the email stuff is all working so let's give this a bit more space open up the network tab to see if our request is successful and by the way we can already check our database if the last request went through by the way to check if that user is in our database and they are not okay let's head in here enter or email enter anything one 23 is the password hit sign up and we do not see the network request which means the sign AB rout has some kind of form action that we don't really want let's get that fixed oh never mind we don't even need to fix that it was probably just a bug I just literally open this window again tried it again and now it's sent through the network request that's weird but it worked um so the form action is gone because what should happen and what is actually happening now is that the handle submit that we use from react hook form is taking care of the preventing default of the form submission action by itself so we don't even need to do that ourself we can just expect the values to be passed into the onsubmit from the handle submit and don't get our stuff put into the URL bar and actually have the onsubmit stuff execute which now it does weird that it didn't but try restarting the server and it should work um just perfect okay so it sent the request what that also means is that we should get an email so let's check here on my right hand side if I got one okay so currently it seems like we don't get the email however one possible fix might be to change the onboarding and rent.com to a dodev and then restart or development server and let's see if that works cuz I might have messed it up and it was actually doev let's close out of this panel give or page a lot more space because that's what's going to be important now and also in our database we are able to see that this user was created and the verification token we need to use to verify this user however for now let's delete them again so we can try the sign up flow once more let's reload our page and enter in the same credentials and then see if the network request is made and then if the email actually arrives in the inbox so let's say admin and then as the pass password let's say anything one 2 three let's hit sign up and see what happens so the network request is actually sent we get the send to email and now let's see if we receive that email in or inbox Aha and I found out why this doesn't seem to work so technically it works but when you look at the resent dashboard which unfortunately I can't pull up for you you have to do this yourself and because it will show like my personal email and but if you look at the recent dashboard under logs it will show why this doesn't necessarily work maybe it even worked for you assuming that what you put in here is your personal email that you signed up with for recent because as long as you use their onboarding email right here you can only send an email to the account that you signed up with on recent so to yourself and that is for them to make sure that you don't abuse their email to send emails to anyone so if you want to send emails to anyone else like for example um you know all your customers it might make sense to register your own domain on recent or for now just for mocking purposes it's also totally fine to follow along with only your own email that you also use on recent everything will work just fine so if I use my email right here that I use on resent to then it will send us the email I'm going to use my hello joshr coding.com that's going to allow us um to send emails to anyone for now but you can also worry about this uh like way later when you go to deploy this application and actually you know get your first users and customers and whatever then insert and verify your own domain on resent and for now just use the same one as you use on recent okay let's restart our um server let's close out of this way too much stuff going on on and let's restart this yarn Dev and now let's try if we get the email right here awesome and this actually worked I'm going to go into the Snipping Tool cuz I don't want to show you my entire email but let me make a snip of this and then move it over so we see the digital hippo email right here and then it says verify your email hello please verify perfect so that works however currently it looks like absolute hot garbage so if I this if I just open this up and then do a little snip of that once again we can see what the email looks like and it looks like this hello please verify and if that is not the most unserious email you've ever received I don't know what is so we don't need to save that but let's improve the email let's make it look genuinely like a professional email that users should receive or actually you know what let's do that later I think let's get the functionality working first and then worry about all the design stuff to make it look amazing and for now let's not worry about it too much so let's just get the functionality in here and so essentially the email that we want is a a tag for example so the user can click on it and let's give it a little H ref right here let's close all of this and also close the aack by the way later we're not going to write literal HTML in here that is horrible developer experience we're going to make it properly look good work good and you know all that good stuff for now to get the functionality working the link let's construct it so the hre should lead to and I think this has to go in quotes right here the hre should go to process.env do nextore server URL so wherever our server is hosted SL verify Dil and then the token we can simply attach that as a query parameter token is going to be equal to the Token that we receive right here in the arrow function and that's it for example we can say verify account inside of this a tag so when we click it we're going to get taken to this domain right here which is going to handle our email verification however the verify email page is something that doesn't exist yet so let's go ahead and create it this lives in our app folder and then let's create a new folder called verify oops and I switched to the English keyboard again and then verify D email like that and let's move this into our off folder because it is authentication related so it should live right in here and inside of this verify email we need to create a page. TSX you know the drill by now so if we now take a look at where we are in the authentication flow it's right here the verify oh Jesus Christ the verify email right here where we have a token and now we're getting sent geez go away man and now we're getting sent to the verifi email page right here where we need to set the verifi to True by the way we don't even need to do that that is done done for us we just need to verify the token right here in the/ verify email route which is precisely where we are right now so the cons verify email page right here an arrow function that we need to export as the default at the very bottom uh the verify email page just like this okay let's return something from here and that's going to be a div not a divide get get rid of that a div there we go let's give this a class name and let's give the entire window more space there we go and pop this up on the right side so we know where we are and the class name is going to be container just like that relative Flex a padding top of 20 a flex Das call an items Das Center a just oops justify D Center and large a padding X of zero and then let's open up this St let's give this a bit more space in case you missed anything in the class name pretty long and inside of here let's open up one more div with a class name of MX Auto Flex a width of full a flex-all a justify Das Center a space y of six and on small devices we want to give this an explicit width of 350 50 pixels there we go we can do that in these angled brackets that's the Tailwind Syntax for it okay let's open up this div and let's move this into a side by side again just just so we have the um you know overview of what we want to do on the right hand side here and then inside of this div right here let's do a conditional check and basically what we want to do is get the token and then render the page content based on the token the token being whatever we pass right here as the token value so what we just wrote inside of the anchor tag now how do we receive that token on the page if the link looks like this well we can simply get the search prams in this page from nextjs we can destructure the search prams these are props that are automatically passed to our page and let's call this the page props so the page props are not automatic we need to declare them the interface page props let's do that right above the component and the search params that we receive in this page are going to be of type object and then instead of here we have in these angle brackets a key that will be a string and the value of this key will be of type string or string array or undefined if it's not passed at all so the key in our case would be token this one right here token and then the value any token value would be either string string array or undefined standard you know query Prem types okay now the const token that we need access to in the email verification which is going to be like the you know random thing that is generated by our CMS this is going to be the search prams do token because we pass it as the token right where was it where was it right here we pass it as the token that's what we call this query parameter so we need to receive a right here as the do token in our verify email page and now we can do a conditional check if we have a token and the type oops not percent and the type of token is going to be a string in that case we're going to render out some jsx else we're going to render out null and what are we going to render out well a div with a class name of grid and a gap of six and then a client side component this is server s side we're going to render out a client side component in here that's going to handle the verification of the token however if we do not have a token well in that case let's quickly replace the null just that was just a placeholder and let's replace it with a nice looking empty State meaning or indicating to the user that we have sent their email you know they should be alerted to hey you know check your email you received a link that is going to happen inside of a div with a class name off and let's put this back in Focus right here where with a class name of flex a height of full a flex-all an items D Center a justify D Center and a space y of one let's open up this div we just created inside of here one more diff with a class name of relative margin bottom of four a height of 60 a width of 60 and a text- muted D foreground perfect inside of this div we're going to render out in image that we get from next SL image and that's going to be a really cool image with the source being as a string the hippo D- mail.png this image is going to receive a fill property there we go and as the all tag we're just going to say you know hippo email send image for example okay great now this image do we have that in our project let's check the public folder for the the hippos email.png and we don't have it so it should be right here in the public folder and to get that image head over to the GitHub repository or I mean you probably already downloaded all the images from the public directory um and this is going to be the hippo send email or email send Oops I messed that up it's going to be hippo D email- send so the other way around not sent email but email sent right here if you click on this image download it drag it into your public folder this is the image we're going to use to indicate that hey you received an email and it looks really really cool so download this image drag it over I'm going to do the same thing drag it over right into here into our project perfect that's what we're going to use on our page as I mentioned in the very beginning of the video I want this to be a complete really nice looking project that you have in the end to not only learn how to architect software not only learn how to write good code but also make it look good while we're at it okay so you're completely free to use this image for whatever you want um you have all the rights to it and it will look really good on our page let me show you that how it will look so let's finish up the page and see the H3 we're going to create right under the closing div for the image right here is going to get a class name of font D semi bolt oops semi bolt and a text of 2XL inside of the H3 let's say check your email perfect right under here let's do a conditional check whether we have the email of the user or not so essentially first off we need access to the email that the user used to perform this action you know and this email we can simply pass as a query parameter as well so the cons email or let's call it to email because it was sent to this email is going to be search prams do to for example right here and now after receiving this to email we can do a conditional check right here below the H3 so if we have this email if we have a to email then we're going to render out a p oops not a package A P tag with a class name of text muted foreground and a text Center inside of the ptag we're going to say we and then an an AOS colon what this does essentially it's the same thing as like the regular colon but in an HTML Safe Way that we can use without getting any errors from HTML we've sent a verification link to and then we can simply insert a span element right here paste in the to email or not really paste in but dynamically inserted into the span and also give the span a class name of font D semi bolt to make it stick out a bit more great after this span element we can simply put a period yet because that's the end and then as the alternative alternative case if we do not have an email and I switched keyboards again holy and we can simply put a colon and we're also going to render out a ptag in the other case however that pag is not going to be specific on the email that we sent this link to because we don't have the email so in that case when we don't know which email we sent it to we can say we and then again apos colon weave sent a VAR verification email or verification link rather to your email period perfect so if we don't have an email we're not going to specify it if we do we are and this pag is going to get the same class name of text muted foreground and also the text Center so it will look the same awesome let's save all of this and head over to our verify email page to you know just kind of take a look what we just did and is our server stopped or why doesn't this work let's restart it and now it works however we get an error failed to parse hippo sent email and that is probably because we forgot the trading or the leading slash to make this you know from our public folder all right there we are check your email we've sent a verification link to your email if that's not beautiful I don't know what is that looks really really nice and if we now mock an email for example you know the to is going to be equal to my email at gmail.com or whatever in the query parameters we've sent a link to my email at gmail.com very very nice okay cool you might wonder Josh that's all nice and good it looks good but we are never actually verifying the email how and when does this happen and the answer is that's going to happen right damn now CU we're going to insert a custom component right in here that's going to say or let's call it verify email just like that this is going to be a client side component where we can actually make sure hey is this email valid or is it not so this verify email component let's go ahead and create it this is going to live in our components folder right here let's create a new file and call it verify email. TSX there we go perfect inside of here let's declare the verify email as always as an error function and Export that as the default verify email there we go and this will be a client component so at the very top you know the drill by now we're going to insert the use client directive to make it interactive now the logic that this component will handle this is going to happen on the back end so we can already go ahead and start destructuring from trpc do off because we know this will be an authentication method and we haven't created the API end point to handle this just yet so let's navigate over to our off router and get started to create this API endpoint that lets us validate a user's email now we can do this anywhere in here for example let's do it below the create user the veryify email API endpoint it's going to be a public procedure because anyone should be able to call it and as the input we are expecting a z. object now inside of this object we need the token that is going to be passed to this page and this is going to be of type z. string just like that awesome as the actual business logic we do that by chaining a mutation which which takes a callback function however of course I need to switch the keyboards again I don't know why I keep messing up the keyboards anyways inside of this mutation right here by the way first off we need to import Z from Zod so we can actually you know parse the input correctly and don't get an error and now for the actual business logic that we need in here so first off we can destructure the token from the input because we know that this will be passed into this API route the input we can simply destructure right here from the mutation because we added it right up here that is the reason why we can destructure it right here if this wasn't there the input right here would be empty because you know of course there is nothing um we get access to now as for the logic it's very simple first off we need access to the CMS client that lets us verify the email so let's say con payload let's call it payload is going to be equal to await get payload client the U that we wrote that gets us access to our CMS and of course we need to mark this mutation as asynchronous to be able to do that now let's say cons is verified it's going to be equal to wait payload Dot and then there's a really you know useful method on here that is verify email which lets us you know Define or off collection so whatever collection we are trying to verify a person on which is of course our user collection which handles everything that is off related and as for the token we can pass in here this is simply the token we get from the input and this errow we get right here is really cool type user is not assignable to type users or CMS payload knows what is a valid collection and what is not so we get full type safety across the front and backend not only through trpc but also through or CMS so we know exactly what we can pass as the collections and what we cannot pass now if this verification operation fails if not is verified in that case we're going to throw a new trpc error which is the recommended way by trpc to handle errors and as the code we're going to use unauthorized this user is not authorized and otherwise everything was successful we can simply return a success oops success of true back to the front end so we know everything is okay the email was verified the logic actually is going to happen in here so because we add added the email verification if we take a look at our users we can see that added a underscore verified well at least it should have let's reload our database and maybe it didn't sync yet because well let's take a look in here there it is we have or users in here and we can see there's an underscore verified this defines if the user has verified their email or not and if this operation oops right here is successful that verified field will automatically be changed by our CMS to be true so we can actually know that this user actually owns the email that they claim to use in this sign up form beautiful and after defining this API route what this allows us to do is to call it from the front end in or verify email component now we can simply call the verify email beautiful with trpc we get full type safety and we can use the use and actually I just noticed we did this mutation but we don't even need this to to be a mutation we can change this to be a query because we're not changing any data we are simply reading data so a query in our case is totally fine to read if the success value is true or not so let's go back and we can simply use the use Query right here as the options this takes the token now where do we get the token from and the answers we have it in the parent you know we have the token right here so we can simply pass it in as a property as a prop inside or verify email component so we can destructure the token right away in our component and for example let's name this um you know the verify email props let's go right above the component and Define those props in an interface and this is called verify email props just like this and now we need to tell typescript what the token type will be and of course this will be a string that we get from the URL and just like that we can correctly make a request to our trpc endpoint and also get the data the is is loading State and the is error State all from our query by destructuring it now if we have an error we want to display a userfriendly state and where is the app there is the app okay let's pull that up on the right side to see what we are changing right here on the left hand side so if we have an error if is error that just means that something went wrong with the token verification maybe it's invalid maybe it has expired we don't really care we're going to show an error message message either way and this is going to be a div with a class name of flex a flex-all an items D Center and a gap of two now inside of this div we can render out something like an X Circle for example from lucd react an icon that indicates to the user hey something went wrong let's give it a class name of height eight a width of eight and a text red of something like 600 to make it look good right below we're going to have an H3 saying there was a problem and this H3 is going to get a class name of font d semibold and a text of XL now right below this H3 let's create a P tag with a class name of text- muted D foreground or you know regular text color and a text of small in here let's say this token is not valid or might be expired might be expired period oops please try again there we go now to get a feel of what this error will look like let's actually change from the is error to if true so we always render this just for a second and by the way we also need to import this component in our page for the verifying and pass it the token as the prop so as for the token let's pass in the token and that works because we made sure it exists and is of typ string right beforehand perfect let's start back up our development server and then take a look at what we just created so let's let's refresh the pinch we can give it a bit more space and let's see what happens because ideally we should see the verify email Arrow state right here on the right hand side of course not just like that but we will only see this remember if a token is passed into this component or this page rather so for example as the end token for now let's just mock it and write end token is equal to something okay that was a problem this token is not valid or it might be expired please try again beautiful okay we know that the arrow State works and looks really nice now let's handle the success state so let's change the true back to his Arrow so we don't always show that but only conditionally and now if we have a data do success which we return from our back end in that case if everything went well and the verification was successful let's return a div at the top level with a class name of flex a height of full a flex Dash call an items stash Center and a justify Center to just put everything both vertically and horizontally into the center in here a div with a class name of relative an MB a margin bottom of four height of 60 width of 60 and a text- muted D foreground let's open up this div and inside here goes in image from next SL image this is going to be self closing and as the source we're going to use a custom source under slash hippo D email- send do PNG we're going to give it a fill property to make it take up as much space as it can in the parent right here and we're also going to give it an ALT tag of for example the email was sent something along those lines just understandable if the image doesn't actually show up for any reason okay awesome let's see if we have that image so the hippo email sent should live under our public folder hippo email sent we do have that image right here and that should show up hippo email send did we type that correctly yes we did in here now currently we are mocking the token to something that doesn't make any sense remember I just typed in token is equal to ASD that doesn't make any sense so let's get rid of the error state for now so we always see the success State just for a moment right below this div let's create an H3 element with a class name of font D semi bolt and a text of 2 XL just like that and say you and then and apos colon your so remember this is just the aposto but in an HTML safe way we can use and not get any linting errors you're all set exclamation point okay and let's see what happens is our server still running yes it is so let's see what happens why are we not able to see this text oh and of course the data will not be successful because as I said the token is currently just mocked so for a second let's replace this with if true and then we should be able to see this stuff show up the image beautiful the text right here and let's add a quick P tag just for some explanation right down here and say for example thank you for verifying your email period okay and let's give this ptag a class name and a bit more space to the entire left side right here a class name and that's going to be a text- oops D muted D foreground once again a text - Center and a margin top of one beautiful okay and also let's offer the user we can see that right here on the right hand side let's offer the user to log an option to log in right away and we're going to be doing that using a link tag from nextjs saying sign in and let's give this an HRA and this is going to lead to/ sign-in and also to make it look good because right now it certainly doesn't let's give it a custom class name this class name will of course you know the drill by now contain or button variants and we can even give it a custom class name in here class name and this is going to be a margin top of four so it's a bit more spaced out from the top so we get a beautiful sign in button right here that we can click and then we should be taken to the sign in page because our account now successfully exists beautiful lastly we need a loading State because right now we have the error State the loading um and the success state but there is no loading State just yet because we want this to be a complete app you know with a really nice user experience we should definitely add that and it's going to be very straightforward for example we can just copy the arrow State and change it up a bit but the layout will be the exact same so let's copy down the aror state and say if is loading then we're simply going to return the same thing that's pasted in there as the success or the arrow State rather not the success one and then in here for example let's change the X Circle to a loader 2 we get from Lucid react and also add an animate Das spin property to make it spin and instead of the red text we're going to change this to zinc 300 it's like a light gray color we get from Tailwind instead of there was a problem let's say verifying period period period and instead of the P text right here let's say this W won't you know and then the end a po colon won't take long period and let's try out the loading State let's say if false for the success State and then if true for the loading state so we can see what it looks like verifying this won't take long and that's true because we will either get an error or we will get the success State and the loading state is just when we are initially fetching the data perfect really really good job let's change this back to the actual values the first one is going to be the is error the second one is going to be the data. success and the last one is going to be the is loading to properly handle all the states that we possibly can in this verify email form perfect let's save that and now the entire email verification process that we outlined right here should work so let's revisit this process just for a second user comes in enters their email and password on the signup page then we are creating a verification email let's take a look at the code where we do that that happens in our off router it happens right here by the creation we are automatically also sending an email the email contains a link just like this with a verify email token that is then going to be attached in the link and send us to the verify email page right here so we're going to end up page. TSX verify email on this page and get access to the Token that is passed from the email like this and pass it into this client site verify email component this is pulling our backend or rather making a request to our back end with that token where we are then verifying the token right here and if this operation is successful then the underscore verified value will be set to true and we know that the user actually owns this email perfect so let's give it a shot let's go into our database right here delete one user and try that again let's click browse collections for me that's going to be cluster zero and go into our users right here see how many we have and let's delete one so we can try the entire authentication flow once again perfect let's head over to slash sign up and try it out with me if everything works correctly let's sign up using any email that we own and then for example anything one two three as the password and hit sign up perfect now one thing we should definitely add which we're going to do here in a second is so we immediately get redirected to the you know verify email page and actually let's do that right now because this is a really bad user experience we click the button and literally nothing happens that's not what we want however I already checked my email and we know this works so verify your email and we get the link that contains our account verification right here let's see what happens if I click that I'm going to click that that's going to take us right here to the your all set page after verifying and we can see the complete token right here in our email and it should work the exact same way for you you're all said Thank you for verifying your email and now we have the option to sign in after creating our account and we can verify that our email was um validated in our database so right now we have one user let's refresh our database and then see how many users we have we should have two and this one should have a verified property undor verified of true perfect the email verification Works however as I said the user experience currently is not the best we click the button and literally nothing happens that's not ideal let's make this super user friendly let's switch back to our main page. TSX for the sign up and in here we can do something on the use mutation because this gets some callbacks for example let's properly handle the error if anything goes wrong we get access to the error in the onerror Callback on the on mutation function for example if the error. dat. code which is optional so that's why the question mark is here if the arrrow do data. code is triple equal to conflict that means there's already a user with this email so we can say toast and by the way I think we already installed the toast Library way earlier so we should be able to just import toast from zoner and yes we did we can simply import that here toast. error which is going to render out a beautiful error for us and let's say inside of this error this email oops is already in use sign in instead question mark just like that perfect let's try it out because we already have the account all we need to do now is to click the sign up button again and we should be able to see this toast however currently we don't and that's because we have never defined where this toast should be rendered so we need to go into our main layout. TSX and in here one very simple change we need to do you can put this anywhere by the way I chose to do this right below the main tag right here let's put the toaster we get from zoner and this component is going to control where our toasts are rendered for example we can pass it a position of top Center this is where we want our toast to be displayed and let's also add a rich color so they will look red or green depending on whether it's an error or a success so as you can see right now this email is already in use we get beautiful TOS notifications very very nice for the user experience perfect now let's continue properly handling this error like a pro for example if the error is an instance of a Zord error a Zod error is a class that is provided to us by Zod and essentially we are checking right here if we correctly pass the input right here if it matches the off credentials validator so if it contains a email and a password that is exact or at minimum eight characters long if it's not then we are going to get back a zult error and where were we right here in the sign up page so we passed the wrong data but we don't have to explicitly say that we can render out a toast. error saying the error do isues at the index of zero. message and this is going to contain the actual Z error so for example the password is not long enough of course or code will already fail here because of conflict and it won't even let me enter a password that is too short that's fine but this is for additional API security and a very very good idea but just in case the user somehow finds a way to mess up the inputs right here they will get the correct error message so we are rendering that out and by the way because we never want to show more than one error we're going to return early in both of these if statements preventing the execution of the other one and then at the very bottom we're going to say toast. error because neither of these cases are actually true but we still have an error we just don't really know what it is we can render out a generic error message something like some something went wrong please try again you know maybe the server has a delay maybe you know anything could go wrong in that case we're just going to render out a generic error right here and then on success what should happen if this all works out well we know we get the sent to email and by the way we need to complete the arrow function in order to actually get the typescript intelligence right here we get the sent to email and now we can render out a to. success if in template strings so we can interpolate here in a second let's say verification email sent to and then the email which we sent it to which is literally right here this sent to email and then period perfect and then we're going to say router. push now this router is something that nextjs provides to us in a really intuitive API cons router is equal to use router let's just do it right above the you know whole trpc stuff right here and this comes very important from next SL navigation not next SL router that is for the old nextjs we're using the new nextjs so we're going to use the use router from next navigation otherwise it will just um throw an error and we are going to push to the domain of / verify dmail and then as the question mark to and this contains the email that we are sending the email to obviously the sent to email so that we can then display this on the verifi email page and we're passing it as a query pram to there awesome let's save everything and delete our user one last time so we can check that the entire flow is working let's head over to a users into the one we want to create let's delete them from the database and try to validate them again so I'm going to use my admin email right here I'm going to say anything one 123 as the password hit sign up that's going to redirect us to this page check your email and then the email they sent it to beautiful and now if I go and check my email here on the right hand side I can see the email and if I now click this link right here verify account then it's going to take us to this page you're all set thank you for verifying your email perfect very very nice we can close out of this we can minimize this or give it a bit less space and now our account was successfully created in the database as a verified user we can quickly go ahead and verify this I know we already did it once but just to make sure verified true here is the user in our database beautiful very very nice let's go to the sign up page SL sign up and finish it up completely because right now when we click this button we do get a red outline but we are not actually showing the user any error that there is with this field now you can probably guess that the email is wrong because it's empty but it would be a very nice for example for the password if the email was valid to show the user what's wrong with it what is the problem instead of just marking it as red and doing that is actually really easy with react hook 4 with the library we use for the forms for example let's go below the email field and say error which is optional by the way errors there we go Dot and then email this works completely with typescript and if there is an error with the email then we're going to render out a P tag right here P there we go containing the errors. email. message just like that and we can give it a class name and this class name oops class name there we go it's going to be text small and then also a text R of 500 because this is an error great we can copy this and paste it right down here below the input for the password same exact thing except for the email we want the password but otherwise it's the literally exact same thing all right fantastic job now we have a beautiful page that lets us know what is wrong by the way let's save that hit sign up and now we can see the actual error password must be at least characters long now where does this error come from if we go to our validators right here this is the error message that we provided that we make if this was like 16 for example 16 characters and we save that it would be that error message that we Define that is why react hook form and Zod go so well together it's a beautiful user experience in the entire authentication flow and I really really like this approach and I hope you do too and use it in your future projects also awesome so we can create our account successfully now the question is how do we actually sign in and the answer is well it's practically the exact same thing so we can copy this entire sign up page and create a new folder in O called sign in inside of this signin folder let's create a page. TSX because it needs to be named that way and literally paste in or sign up page and if we save that then we can already navigate to the sign in page and it's going to be the exact same stuff now again we need to change some details for example the you know create an account that does make a whole lot of sense already have an account sign in well that's exactly where we are so that does make a whole lot of sense as well and of course instead of creating a user we want to sign in a user but as you're going to see that's really straightforward first thing let's adjust the UI a bit so for example instead of create an account let's say in here sign into your account perfect okay then instead of the or already have an account signed in let's say something like don't and then apos um quote you already know that not quote uh semicolon you already know the drill and don't have an account question mark and let's save that and see what happens sign into your account don't have an account and then we get um oh by the way we need to change this link from sign in to sign up if we don't have an account we don't want to be redirected to this page but of course the sign up page and now we can navigate back and forth beautiful now on this side sign in page there is one option we want to offer the users to do and that will happen at the very bottom of the file right below or sign up button which by the way we also need to um change to sign in so it makes sense on this page and this is going to happen right below the form so let's go below the closing tag of the form and create one div with a class name of relative in here a div with a class name of absolute in inset of zero inset of zero there we go a fleux and an item St Center inside of this div we can create a self closing span element right here just for decorational purposes with a class name of with full and a border DT and because this is purely decorational you can go to the parent and add an area label um no an area hidden and this is going to be true just as a kind of be best practice for accessibility great right below this div containing that sell closing span let's create one more div with a class name of relative Flex justify D Center text- XS for extra small and upper case it's going to force all the characters in here to be you know as the name might suggest in uppercase and here goes a span element with a class name of background back well BG background I guess BG background means background background then a padding X of Two and a text- muted D foreground or trusted you know actual text color saying or so what we just did is a visual separator between the signin button and what's about to come next because next up we're going to render out the button that allows people to sign in as sellers instead of regular customers and allow them to sell on our platform and this will happen on a conditional check so for example what does that mean we are GNA pass the state of whether somebody is a seller or not as a URL query parameter into this component that is why we need to receive it whenever this page is loaded so just like before we can say const search prems is going to be equal to use search prems this is the equivalent right that's what I meant by just like before of receiving the search Prem server site what we did before for example in here that's how we receive them server side and now we are receiving them client side just like this using the US search prams hook it's a utility that nextjs provides to us from next / navigation we also need access to the router con routers equal to use router again very important from next SL navigation oh and by the way we already declared it down here so we don't need to duplicate it and let's now find out if this person is a seller by saying con is seller is equal to and when are they a seller when the seller pram is in the search prams so we can say search prams doget and which do we want to get let's say as because that is how we're going to call this parameter so in the URL it's going to end up as something like know for example um sign in oops sign in question mark as equal to seller just like this and this as is the query parameter and the seller is the value it's a key value pair that we can access right here on the left hand side using search prems doget and we want to check if this is triple equal to seller so if the as seller is in the URL bar then this is seller will evaluate the true and otherwise it will be false or undefined if this doesn't even exist because the search PRS you know don't have to be passed they don't have to exist it's also determine the origin const origin is equal to search prems oops. getet and then the origin this is going to be really useful later when we want to redirect from for example the card page to the sign in because only authenticated users should be able to access the card but we want to redirect them back to where they were after they sign in successfully this is going to be an amazing user experience and so we're going to do it in this app because it's actually pretty straightforward perfect and that's all the access we need in this component now let's actually sign in a user so we're going to call trpc do off. signin of course that's a method that doesn't exist yet so let's create it and the logic is going to be pretty straightforward let's navigate over to our o router that handles all the authentication stuff and in here actually declare a backend API route that lets us sign in the user of course this should be a public procedure anyone should be able to call this because you can't log in if you need to be logged in to log in you know does does that make sense anyways anyone should be able to call it nevertheless so we need to make it a public procedure with the input being the O credentials validator that we also use for the sign up and the mutation to handle the actual business logic that should happen on the back end let's give it a bit more space just like this and as the logic first off we need to destructure some things from the input because we need access to the email and password that the user entered on the signin page so let's destructure the input from the mutation where we get access to it and now we can destructure both the email and the password from that input and now let's actually log in the user that's going to happen inside of a TR catch block where we catch the error and can handle it accordingly and in the try we're going to say await payload do login now payload is or CMS we don't have access to yet let's say con payload is going to be equal to await get payload client not get payload just like that up here and Mark or mutation is asynchronous so we can even await an operation now this payload do login takes or collection and of course this is going to be or users where all the authentication stuff happens and as for the data we can pass in our email and our password that we are passing from the front end from the signin field right here the email and the password that gets sent in into the input and then into our login function right here however there's one little caveat with this approach this would actually log in the user but it would not actually set the cookie that is equivalent to the email and password so whenever you log into a website essentially what is happening for the off flow is check this out essentially all all a login is is an exchange from your email and your password which you're sending over to the server right here so you make a request with your email and with your password let's say you and you communicate with a server you send along this data and the server gives you back a token maybe a Json web token maybe a database session ID doesn't really matter as long as you get back a token from the server and this token is nothing else than the equivalent of your email and password stored as a cookie you can validate that with any app if you're signed in can go to inspect and then into the application right here and then in the cookies you're going to see a login cookie or not right now we're not logged in so there is no cookie but I'm going to show you later there is going to be a cookie that essentially is the equivalent of our of our email and our password so that's how authentication works that's really all it is and right now what we are doing is basically everything but this step we're logging the user in on the server just like that that happens right here after we sent along the email and password but we're not actually setting the cookie and to be able to set the cookie we need to pass in the request object into this login operation that's going to do all the logic for us however now you might wonder Josh where does this request come from and the answer is this comes from Express so let's close out of a lot of these windows and there really is no black magic about this we can simply attach the request object to our context the method we used to transfer data from Express over to nextjs and trpc and we can simply attach the express request as if it was any kind of data in fact if you check the create context we are already doing that it's just typescript not recognizing where this comes from because originally this will come from the context that we can destructure in the mutation as well the context and if we try to destructure from the context we can see typescript doesn't tell us anything about what we can destructure while technically we should be able to see the request and the response and that's just a typescript thing so in order to tell typescript hey we are actually you know we actually have access to this stuff we need to export the type Express context and this is going to be equal to the infer async return type we get from at trpcs server essentially just a typescript utility that lets us infer this code context and we can pass in the type of create context the function we have right above and we can now use this Express context in or trpc initialization for example under Source trpc where we create this context if you're kind of lost where we are it's in the file system under trpc trpcs right here and this context creation actually takes a generic where we can pass in the express context to tell typescript scrip what kind of context we will be dealing with so let's see if this worked let's go into our off router there we go words are hard and now we can actually see we have the request and response however one thing you might not immediately notice is that under the API trpc route this will actually complain because it thinks that the context is an empty object and this is fine everything works by now this is simply typescript not recognizing that the actual context comes from Express and not from here so this is an error we are totally fine to ignore so let's say at ts-- error and let's say for example context already passed from Express middleware we are totally fine to do so the context actually works we can ensure that by trying it out right now and this is simply an error from typescript which doesn't recognize that hey something might be wrong which really it's not so it's very safe to ignore and just like that we can destructure the request from our content text and actually I'm pretty sure this needs to be the response so what we essentially just did let's move this into a side by side is now we actually attach a cookie that contains or authentication token to the response that is then sent back to you as the client and saved in your cookies so the authentication session will last and lastly let's return a success of Tru from this API endpoint and inside of the catch we can simply rethrow a new trpc error that's going to handle the case where for example the email or the password is incorrect and as the code that we're going to throw in here that's going to be unauthorized you're not authorized to log in with a wrong email or wrong password whichever or both maybe if the user is really trying to troll You Beautiful let's save all of this we can close out of all to clean up our workspace and go into our signin page to finish up the logic now we actually do get access to this sign in right here and can handle all the cases accordingly for example let's let get rid of all this stuff right here all the callbacks that we have and start over the on success what should happen when somebody logs in successfully well this takes a callback function and let's say toast. success and for example say signed in success fully then we're going to say router. refresh this is going to refresh the page and for example in the nav bar where we will have a state of logged in and logged out and show different stuff that's going to make sure we show the actual fresh information in the Navar that's why we have it and if there is an origin where the user was redirected from for example if you want to access the card and then get redirected to the signin well we want to send them back to where they were for a good user experience so if there is an origin we can say router. push and we're going to push as a template string to slash and then that origin to send them back to where they were and if the user is a seller if is seller in that case they're logging in as a seller and should be pushed P via router. push to the seller backend where they can insert their products to/ sell and in both cases I'm holding alt to Mark a cursor at both we are going to return because we want no further code execution after the origin is done we don't want to push anything else for example and if none of these cases are true and the user is just logging in in that case we're going to push them back to the homepage that lives under just slash perfect now for the eror case right which is very important for a good user experience very simple error handing right here we can receive the error in the function right here in the um arrrow function and say if the error. dat. code is unauthorized right here unauthorize that we are throwing on the back end to properly handle this in that case let's render out a toast. arror and say for example invalid email or password or whatever error message that you want right in here beautiful as for the onsubmit we can actually give the mutate a more descriptive name this works because it's the exact same thing as calling it a different name and then calling the different name but let's name it sign in for example it's just a bit more clear and then on submit we're going to call the signin function with the email and the password at the user entered and now to finish up this last part where we have the or remember we want to offer the user the option to sign in both as a customer and as a seller so let's quickly finish that because this is going to be very straightforward logic okay right here with two closing divs after the span element let's do a conditional check if the user is trying to log in as a seller in that case we're going to render out a button element or or custom button and in the other case we're going to do the same the only difference is that the first button is going to say continue as customer and the second button right here will say you can probably guess it continue as seller just like that perfect now what should happen when you click this button on click something should happen and we already know what should happen because if you think about it we are getting the state of somebody being a seller or not from the URL that means whenever we want to log in as a seller or a buyer all we need to do is change the url and everything else is handled for us so for example the seller scenario cons continue as seller a function we can call on the button click is nothing more than an arrow function where we say router. push and what do we push the question mark oops in regular quotes the question mark as equal seller that's what we are putting in the URL which is going to refresh our page and show the actual State perfect and the same thing for the con Contin continue as buyer just like this the logic is going to be the exact same thing only that we are going to push something different so let's say router. replace play and now essentially what we want to do is get rid of the seller that's all we need to do and to do that we can simply pass the slash sign in as the first argument in here and then an undefined so we are putting all the query parameters to undefined which is the initial state which is the default you know the buyer is the default and then if you want to log in a seller you can do that just by calling this continue a seller function both of which we can now insert in the buttons at the very bottom of the page to make them handle the correct Logic for example the continue as well that shouldn't be seller the first one should be buyer let also while we're here give this button a variant of secondary just like that perfect and also a disabled value of and this going to be equal to is loading so if we are currently logging in this button should show a loading State same variant same disabled State for the other button let's copy and paste that right in here and then as the on click we want to happen on the second button that's simply going to be the continue as seller and right here what we're doing is a really cool skill to have that you want to know putting stuff in the URL is really really nice because wherever we are in our application we can always change the sign in state based on whether the person is a buyer or seller and that's not saved in local state here but through the URL so we can redirect them to the correct State later on very very cool skill to have if we now click continue as seller we can see that the domain changes and the button also changes now the last thing that we want to do is also reflect that change right here in the sign into your account for example we can say when there's a seller login sign into your seller account very very easy change we can do right here in the H1 sign into your and then let's uh cut out the account and do a check if the is seller is true in that case we want to push the seller into into the string and else nothing and then simply account let's save that see what happens sign into your seller account sign into your account beautiful so now is the perfect time to actually give it a shot and see what happens we already know that we have an account let's for example use my hello Jos coding.com email and then uh anything one to three was the password let's H sign in and see what happens ideally everything should work but it seems like it doesn't do we get any error in the console seems like nothing is happening tier PC oh we probably forgot to refresh or backend we changed something in the off router which is not being watched by nmon so we need to manually restart our server or what we could also do in the nodemon config we could add the off router to the watch for example through a glob um pattern like this or whatever um but let's just reload the um development server and then we should be good to go let's try this again again let's open up the page in full screen and hit sign in invalid email or password did I use a did I use another password all right I entered another password but I do remember it for this account and this was anything one two three so let's see if that works sign in signed in successfully beautiful and we don't get redirected when we should really get redirected oh there we go the development server is just a bit slow this works almost instantly in production by the way so there's no need for a loading St or anything and this is generally really fast just in production it's a bit slower and but we do get redirected successfully to the Local Host 3000 awesome one thing that doesn't change right now is the nav bar so for example it's still offers us the option to sign in or create an account and ideally we should see something like a drop down with or account that allows us to log out for example and we should see no sign in no create account that just doesn't really fit in nafar when we are already logged in so let's go ahead and quickly fix that let's navigate over into our nav bar because right here we're marking the user as null when in reality we can now actually get the current user now in order to do that we're going to define a utility function that we can reuse across the entire application that's going to get us a user on the server side in our lib folder for preparing libraries let's create a new file and call this payload duts do dots not DTS there we go and from here we're going to say export cons get server site user simply a function we can call from the server site asynchronously it's going to get us the currently logged in user now is the argument or no the parameter I keep mixing those up as the parameters in here we're going to accept the cookies and these are going to be off type next request right here A type we get from next SL server at the index of cookies just like that or oops or these are going to be of type readon request cookies just like that and by the way of course this does not go into the function body like I did here but this goes inside of the parameters where we accept and tell typescript you know what type this is and the read only request cookies we have to Auto Import this because the import is super long and very unintuitive but we do need that type in here so this comes from you know next disc server web spec extension adapters request cookies yada yada yada super long import let's just automatically import that and I um you know hope you do too because typing that out not so fun anyways let's get access to the Token in here that we need to call um in order to get the current user so this is going to be the cookies doget and we're going to get the cookie called payload D token in authentication token that is as I showed you earlier automatically set in the response from the server we can now verify that that in our application there's a payload token this is essentially a username and password equivalent the token that we get passed back from the server right here onto our client that is then attached to every request that we make like a cookie usually is and we want the dot value of that cookie and not the name of it great now to fetch the current user very simple cons let's call it m res because essentially it's a response for a request that you know gives us us so a me user I guess we can call it and this is going to be a wait fetch a regular fetch request that we're making to an endpoint under in template strings let's dynamically insert something the process do oops env. nextext corpu serverurl SL API users slme by the way this is not an endpoint that we have to create or CMS automatically creates this endpoint for us we can simply query it or fetch from it to get the currently logged in user as easy as that and as the headers let's pass in an object as the author authorization header goes or token or currently token and then the endpoint will tell us if it's valid or not so in template strings let's say for example JWT or Bearer you could also use but let's use JWT and then insert dynamically the token value that we pass into the get serice at user function as the response we're going to get the user so we can right away destructure the user from the await M res. Json so we can convert that to ajason and from this whole operation let's wrap it in parenthesis after that is awaited and done we can simply give it a type so this is going to be of type as user this is going to be of type user we get from payload type so a custom or custom user and by the way remember this needs to be um relative and not absolute so we can say dot dot payload types so we're not going to get any problems here on the back end and then uh this could also be null if we're currently not logged in if there is no user and now what we simply need to do is return that user right here we could export or you know return it as the default however let's do it inside of an object so we can easily get access to it for example right here in the nav bar so only to tell if a user is logged in or not we can simply Now call the is equal to await get server side user utility function that we have just created together and if we mark this Navar is asynchronous this is a react server component so we can do that and perform asynchronous actions in here we can simply get the user now we also need to pass the cookies into the function and these cookies let's call them next cookies come from cookies it's a function that we can simply call and invoke and this function the cookies let's import that from nextjs that is provid Prov it to us for completely you know free I guess from um next SL headers it's a utility we can simply take and use from nextjs and then pass the next cookies right here into our get server side user to get the actual user if they're logged in or not for example um if they're not logged in there going to be null and if they are we're going to have the user type to work with in the Navar perfect let's save that and see if it works correctly let's navigate over to our local host and give it a bit more space we haven't done the mobile integration just yet and let's reload the page and is the dev server running yes it should be great and this is the logged in state we can see the um sign in button disappeared we can see the sign up button disappeared now there's kind of nothing we're going to fix that right now with a little drop down but we can see the state of whether the user is logged in or not that we are determining in the server side side component Works beautifully as expected that is really really good news and what that means is now we can offer the user a drop down where they can go to their seller dashboard or where they can also log out because currently there is just no option to log out and this option this drop down is going to live right here where we have the create account conditional if there is a user this is going to be the item on the left of this separator if we give it a bit more space this item is going to live right here in the Navar and we can simply call this anything we want for example the user account nav because that's what it is it's a user account navigation menu so let's call it that it's a component that doesn't exist yet so let's go ahead and create it this going to live in our component folder let's call it user account na. TSX and as always this is going to be an arrow function cons user account NV is going to be an arrow function that we can simply export default at the very bottom just like this perfect by the way this also needs client site into interactivity you know the drill by now let's add a use client in here and the drop-down for this component which is going to be essentially is not going to be self-implemented we don't have to do that this is already provided by our UI Library as the drop down um right here so this is kind of what it's going to look like a bit bit different but kind of what it's going to look like um it's going to be fully accessible look amazing all over the box and all we need to do to get it is to say npx Shad CN Shad cn- UI at latest add dropdown and hit enter that's all we need to do that's going to install the drop down component completely for us and we can then and by the way not found did I oh little typo there it's not Shad uh Shad ncn it's Shad cn- UI add drop down perfect well as I said that's going to install the drop down for so we can simply use it fully accessible good looking out of the box in or project selected components not found exiting why is that the case oh because it's not just drop down it's drop down Das menu as we can see right here in the installation okay anyways let's install that and give it a second to load that shouldn't why Jesus man wow so now I copy the typo command Shad cnii at latest add drop down Dash menu or you can simply just copy this instead of doing all the stupid stuff like I'm doing just click copy paste it into your command line and that's finally going to install our drop down menu that we can now use inside of our user navigation nav to make everything look really really good perfect and after that's done we can get started using it in our user account nav the topmost thing is going to be the drop- down menu component itself from or UI right here where it put the component inside of here goes a drop um oops a drop-down menu trigger so the item that if we click it will open the drop- down menu this is going to get an as child property as I told you about earlier that we can use to render out a custom button that's not going to be wrapped in another button and as the class name this is going to get overflow Das visible just like that perfect inside of this trigger let's use our custom button from our UI component saying my account and this button will get a variant and that's going to be ghost and it will also get a size that's going to be small and it's got going to get a class name of relative just like that if we format this that's going to move it to the new line and now after the drop- down menu trigger we can Define the drop- down menu content so what will show up when the drop- down menu is going to be opened this drop down menu content gets a class name and this is going to be a BG of white a width of 60 and also we're going to align it to the end just like that let's open up the drop- down menu content and inside of here create a div with a class name and that's going to be Flex oops Flex item St Center adjust oops justy Das start a gap of Two and a padding of Two And by the way we can already go ahead and import this user account nav in our nav bar to take a look at what we are creating live and it should show up if we give this enough space let's reload the page and our Dev server is probably not running okay so let's restart the dev server and then we can kind of in real time see what we're doing here on the right hand side there we are my account and if you open that well there is currently no content for the drop down but we can click it it's fully accessible using the keyboard for example when we press Escape it closes that's all we want for now okay awesome actually we can give this a bit more actually no let's leave it like that and continue in the user account nav by opening up this div we just created inside of here goes one more div with a class name of flex flex-all a a space y of 0.5 and a leading Das none if we open up the div let's put in a P tag in here with a class name of font Das medium a text of small and a text- black and this text right here should display the username let's just put in the username as literal text right here for a second that's where it's going to show up however we don't really have the username or rather the user email which is going to be the username equivalent where do we get that from well we have it in the parent the Navar has access to the user so we can simply pass it down as a prop into our user account nav right here so all we need to do here is receive it we can destructure the user from the props and this going to be we can even inline this type because it's going to be very simple the user is going to be of type user that we get from our payload types and that's it we can simply pass it now from the parent from the Navar inside of here the user is going to be user that's literally it that was super simple now we have access to the user in the user account nav and for example display the user. email the currently loged and user email in this component right here and that's going to show up the email of the currently loged and user beautiful very very nice after that let's add a separator a drop- down menu separator this is going to be a self-closing thing by the way and the uh down down okay drop down I was wondering why the Auto Imports didn't work we can also import this this is a pre-made component for us after that let's create a drop down menu item just like that also with an as child property because in here we will Define a custom element that's going to handle the um logic and that is going to be nothing else than a nextjs link inside of this link let's say seller dashboard and of course you can probably imagine if we want to navigate to the seller dashboard this is going to be under SL cell and endpoint we are later going to protect where if you're not logged in you're going to get redirected to the signin page right perfect okay and as the last item inside of here we want to allow the user to log out also in a drop- down menu item just the same with a class name of and this is going to be cursor D pointer to make it look like something you can actually interact with and click and as the text we're going to say log out beautiful let's see what happens our account seller dashboard log out very very nice but currently when we click log out nothing happens the menu closes but we didn't really add an onclick Handler to handle any of the logic that should happen when we try to log out and that logic is going to be pretty straightforward actually and we can simply go ahead and create a custom react hook to handle that logic for example if in our hooks folder let's create a new file and call it use- off. TS if you've never done a custom react hook trust me it's not hard it might seem intimidating but really it's not let's export a cons called use off it's important that it's named use custom react HS have to start with a you know prefix use so use off use text use whatever you know this is going to be nothing else than an aror function that now has access to all the react apis because it's prefixed with you what does that mean for example context State use effect all that works not in regular functions if they're not inside a component but it does work in hooks anyways just for your information we're not going to make use of that in this hook because we don't need to we are going to define a constant that is called sign out and this is also going to be an arrow function and the only thing we're going to do in this sign out function let's mark it as as synchronist by the way is make a fetch request this will happen inside of a TR catch block so we can properly Arrow handle and as the you know logic in the tri block let's say conr is going to be equal to await Fetch and we can simply nope just a regular plain Fetch and we're going to make a request to in template strings the process. env. nextore server you know URL slash API slash users SL logout again an API endpoint that our CMS provides to us that we can simply call it's going to invalidate the token remove it from the response and then the user is logged out properly very very nice as some options in here inside of an object after it we can pass for example the method that is going to be post as the credentials we're going to say include and lastly as the headers we're going to send along the content-type headers oops content-type and these are going to be application SL Json a data format that is probably the most common in any API calls so essentially we're just saying hey you know this is of type application Json this could be plain text or whatever but we are working with Jason here okay if the response is not okay if not res. okay if anything went wrong let's throw a new error we don't need to be very specific on the error message here because we're going to throw a generic error in the catch for example if anything goes wrong in the catch let's say toast we need to import that by the way so we can use it in here toast. error and say something like couldn't oops not Cloud couldn't there we go sign out please try again if anything went wrong we're not going to be too specific in that Arrow message and if it did work then we can say toll St success for example signed out success fully so the user knows nice that worked and we can simply say router which we can call the push method on and by the way we still need to you know get access to that router const Router is going to be equal to use router so we can then use it to push around the user and again this comes from next SL navigation not next SL router and just because we um named this use off we can now get access to the apis that's what I meant for example also other hooks that's also a part of the react apis now where do we want to push the user once they have signed out of course to Des sign in page page so they can sign in again and we clearly communicate that hey you are actually logged out that's why you can see this page by the way a page that we will also protect later on where logged in users will not be able to visit it because that literally doesn't make sense if you're logged in you shouldn't be able to see the signin page again only if you're logged out and then we're going to say router. refresh to show the most actual um the most recent state of the data for example also for the Navar so you can see the logged out State again um after you performed that action and that's all that's all we need to do for the US off and logging out correctly we can now simply export this function from the hook for example return and then um sign out and let's do that inside of an object and I switch keyboards again that why that's why it didn't work let's return the sign out function from the use off and we can simply call that whenever you push this button the log out button in our user account NV so we simply need to destructure from the use off hook we just created import that D structure the sign out um function from it and simply call that whenever you push on the log out button at the very bottom so let's add an onclick Handler to it and onclick we're just going to sign out that's all that's the logic it's not Blackmagic it's very straightforward actually and let's see if it works let's refresh the Page hit log out and it says signed out successfully and redirect us to the sign in page by the way we can also check this manually to see if it worked correctly if the cookie was removed so right here the token that was there once we logged in is removed if we log back in anything one two 3 sign in then the token will be reset here it is and it will be invalidated and removed once we hit the log out you can see cookie gone invalidated perfect that is a really nice authentication flow and you're doing a really really good job following along and I sincerely hope that you learn a lot in this video and with that all working we can get to the you know it sounds weird but I guess the meat of the application right I know how that sounds okay whatever you know what I mean we can get to the real part of the entire project and that is basically the whole product functionality so what does that involve let's take a quick look here together so the app oops application architecture let's look at it from a backend or um you know database kind of perspective what exists how do these models interact with each other so what does that mean the um core model we have is a user everything that um you know our app does and you know logically happens in our app depends on a user now a user can both buy and sell products on our website so products there here we go let's put that here one user can list products and they can buy multiple products so what does that mean and actually let's write this out the user right here because I don't want you to just copy my code I want you to learn um how to actually architect these kinds of projects and websites for yourself that's really important to me that you you know get a good understanding of why we're doing what we're doing so each user can both buy and sell a lot of products buy right here and they can also sell products if they wanted to right here and for each user we can store the products they um own that they listed in our shop simply by ID so for example if a user created product one we would store that ID not the entire product just the ID with that user so if we filter anything by the user we also know which products they created which is going to be very helpful later on when we get to the access rules you're going to see what that means basically it's to protect our entire application from unwanted access okay so each user can buy oops and sell of course multiple products as they wish now each product can contain two different things and that's one the product image oops just like that and that is going to be the image or it can actually be multiple images that are going to be featured in the store that users are able to see on the store Front and products can also contain or one product can can contain a product file Now where's the difference between a product image and a product file you see the product image it's what's being shown on the storefront and the product file is the hidden file that is attached to each product what a user actually buys when they purchase the product like the actual icon set file the actual UI kit um you know SVG file or whatever not just the storefront image the product file is completely independent of the product image so this is on the front end in the store the product file is what users actually buy that's what they pay for that's a very very important um difference in our app and just for semantic you know Clarity let's change this to product because a user can have multiple product and each product has an image and um a file you can have multiple images you can have multiple files that's totally cool we don't have any restriction on that but this is essentially the flow of our application user buys product or user sells product and each time that happens when a user buys a product there is something created called an and let's type it in here an order so when a user buys a product right here an order is created and also of course when one user buys a product that means another user has just sold a product so that's going to be saved inside of the order let's draw an arrow right here from this buying product process let's just for Simplicity just attach it to the buying process an order will be created and this order actually contains information about the products that have been purchased for example product one was purchased in this order and product two was purchased in this order and also of course how much each product costs and the total so we can later um you know display that to the user and also charge them for that amount and this order once the payment has been successfully verified we will will know that if we have actually received the payment or if the user has not completely paid yet and once that is completed then and only then will we send the user to the thank you page that contains the um download to their asset so slash thank you that's going to be the end of the purchasing flow because we now know the user has paid right here in the thank you page we successfully have received all the money for the order and the transaction fee and this is going to contain the asset download a secure download link where you need to be logged in and you can only download where we are 100% sure what you actually paid for we will make sure of that don't worry about it we also handle Security in this you know video in this project whatever you want to call it so this is the application architecture users product each product has an image in a file or multiple of those if you want and whenever something is purchased or sold sold which is you know for one person it's purchased for one for the other it's sold it's kind of the same thing even though it's really not but anyways for each purchasing action there is an order created that is then paid and once we are 100% sure it's paid they will be able to securely download their assets that is the application architecture and I think it's really important to talk about it because most videos just skip the step and you just copy their code without really knowing what to do and that's not what I want for you I want you to gain a deep understanding of why we're doing what we're doing why it's working and why we are constructing the application in this way that we're doing it so that you can always use this knowledge in future applications in your job whatever doesn't matter as long as you have this knowledge on how to properly architect smart or how to smartly architect um your own apps um you're going to be in a very very good spot professionally and in your coding you know whether it's a career whether it's a hobby doesn't matter I I just want you to know how this stuff works and why we're doing it okay so let's head back over to our application we did the entire authentication flow we know it works we know the verifications emails the verification emails come through so now is the perfect time to get into the actual modeling of what we just did here so what is a product what is a product image what is a product file what is an order those need to be tables in our database essentially and right now we don't have those tables and the way we create tables maybe you remember from the um collections that we have in the payload config right here essentially each collection that we have like the users are nothing else than a table in a datab base it's as simple as that so if we wanted to create a new table for the products we would do that inside of a collection for example let's create a new collection together let's create a folder because this is going to be our most important collection and this are let's call this folder the products because everything in our store will re revolve I think it's called the English word around products they are super Central to our app inside of here let's create a file called products. TS and this is where the magic will happen well honestly it's not really magic it's pretty straightforward and you're going to see exactly what I mean so to get started in creating this table let's say export cons products and this will be of type called collection config that we get from payload and all that type annotation does is let us know what we can even pass in here what is valid to pass in here for example we need to pass the slug this is going to be products so mostly it's just the collection name in lower case um that's kind of a convention to name your tables for the admin we're going to say use as title and this is going to be the name so in the admin dashboard we're just going to use the name field that we're going to create here in a second for the default value and we're going to do the access rules here in a second let's leave this as an empty object these are really really important this essentially defines who can access which parts of which products can anyone download a product no probably not right and all that we will do in the access controls right here in a second okay before we do that let's actually get started in defining the fields that this table essentially is nothing else than a table in a database will have okay for example each product will have a um user right each product should be associated with a user because that is really really important who created this product we need to know that so as the name let's say user SD type this is going to be a relationship so we can um you know connect the users table to the products table um by what we're doing right here the relation to we need to pass in here is going to be the users so that's nothing else than our users collection over here the first collection that we have as slck it's users so as the relation to this is also users let's require this very important because we always have to have a user with a product otherwise we could have unknown products and we don't know who created this and then has many is going to be false right here because of course one product cannot be created by multiple people and as for the admin we can pass it a condition of false and all this is going to do is hide this field from the admin dashboard that's literally all it does we won't be able to see it and that's you know pretty much already it okay then each product will have a name so let's pass the name as the name the label which is going to be visible in the admin dashboard is going to be name and the type is going to be text and also let's set the required value to true so every product definitely has to have a name by the way before we continue here let's already see what we're doing let's include the product in our payload doc config.sys products or products collection if we tried to create a new product we would see the name field right here because that is exactly what we added and the user relationship field is hidden because we added the conditional false otherwise it would also show up right here but we don't want our users to see that it won't mean anything to them very very nice we now know this is working as the second thing we're going to pass a descript description this is going to allow sellers to describe what their product does what it's good for so so the type is not going to be text but it's going to be text area you could also choose a rich text Rich text that allows um for formatting like bolt and underline and you know um line breaks and all that good stuff but you need to render that out in a kind of more complicated way the payload documentation for example has great resources on how you can do that and but for this video it would be a bit too much so we're going to go with a simpler and easier option this is just going to be a text area and as the label let's say product details this is what users will actually see in our app just like the name property right here that's the label and in this case it's going to be the product details okay very important we need a price what does it cost so name is going to be price label is going to be price in USD in US Dollars the minimum is going to be zero and let's say the maximum is going to be 1,000 SD type let's say number and S the required property let's say true because each product has to have a price 100% okay next up categories as the name let's say category as the label what the people will see in the admin dashboard let's say um category with a capitalized c as the type this is going to be a select and now as the options right here the options the users can choose from for their product this is nothing else than or product uncore category ories we have already defined the categories that we want to have on our website remember so we can simply map over these do map and for each product we can destructure here in a second let's return a object directly so in parenthesis the object right here so a direct return and we can oops let's give this a bit more space we can destructure the label right here and the value oops not the featured and the value and simply return them implicit or directly right here um just like that so we can pass that into the options okay and lastly the required value is going to be true as well because each product has to have a category we need to know where um do we listed awesome okay now let's get to this part each product has a product image what you will see on the storefront on the front end what users will see and then the product file what they pay for we don't have that logic in our app so let's get started with the product file what users actually pay for in the end the name is going to be product underscore files the label is going to be product file and then we can put like an s in parentheses so it's optional you don't have to pass multiple the type is going to be relationship the required value is going to be true because of course if you pay for a product there should be a file at attached to it the relation to is going to be the productor files this references a collection that doesn't exist yet so we're going to create it right here in a second and then let's say has many in our case we are going to set this to false what this means is that each product will have exactly one product file if you wanted to allow for multiple product files like for example icon set in multiple different file formats all you would have to do is change this to true in or let's leave it as false I think just for the demo it's going to make it a bit easier if we just have one product file but again if you want to multiple all you have to change this Boolean right here very very straightforward okay now everybody can list a product on our store which does not mean it's good we want to make sure that the products that end up on our store are good and verified by admins therefore let's add another field called approved for sale and admins are the only ones that can change this users cannot change it the label is going to be product status so this is what the users are going to see and the type is going to be select right here so the admin will be able to select is this fine or is it not fine the default value oops default value we're going to pass in here is going to be pending so not accepted nor denied it's in a pending State and as for the options the admin will be able to choose from or U as the admin will be able to choose from this is an array of objects each object needs a label and a value as the label first let's say pending verification and as the value let's just say pending perfect as the second object in here let's say as the label this is going to be approved and as the value this is also going to be approved just like this so you as the admin took a look at the product and said hey this is appropriate this is a a good file a good product that we want to list and allow on our store otherwise the other case is that it's not good so in that case let's add a denied field the label denied and the value is also going to be denied just like that awesome now very very important not everyone should be able to change this if you let users change this essentially it would be completely meaningless only admins should be able to you know uh change this value and we do that using the access policy now this is something that the CMS payload we're working with um you know kind of provides us some help on how we can configure this so for example what that means in practice for example the create field right here this is actually not a Boolean but it is a function and the purpose of this function is so that we get the request that means we can do a check before we actually determine is the user allowed to do this or not and this request actually contains the role of the user for example if they're admin or not so what we can do is just check if the rec. user. roll is triple equal to admin because if this evaluates to true this means the user is in admin and they can in fact you know create this field we're going to copy this down change it from create to read and then copy it down once more and just change it from read to update so essentially all we did here is say only admins can create only admins can read this field and only admins very important can update this field as well so regular users cannot but you as an admin can regular users can only see the status for them to know hey is my product approved or not but of course they should not be able to change it that would be horrible in terms of security okay after we got that that done each product should have a stripe product associated with it where we can handle the checkout data for payments very very important so let's add that as the name this is going to be the oops not sprice the price ID as the access nothing should be able to change this same thing as before create but in our case we're just going to return false nothing should be able to change this except us on the back end when we call the get payload client get payload client where we get or CMS anywhere we do this the get payload client right here we can overwrite these AIS settings there is a specific command we can uh use for that which is override access true but already by default when we get the payload client this um will overwrite the access Fields so nothing no user no admin will be able to change this except us through code essentially and that's the only place where we need to change this so this is a really good idea to set the access policies to Falls because we know we never need to change them and we should never change them unless it's specifically through the M code that we're going to write to interact with stripe same thing for the update always just return false for the price ID because it's a field we receive from stripe later on that allows us to associate the product with stripe okay as the type this is going to be a text field regular string and in the admin panel we're just going to set the hidden property to true so you should not be able to see this in the admin panel At All by the way this is also a function I believe right no this is the okay this is actually a Boolean so let's say oops hidden to true and let's leave it at that most of the stuff is a function this property isn't um let's leave it at that so it won't show up in the admin dashboard okay we're almost done let's add the stripe ID and we can actually simply copy down the price ID it's going to be a very similar field however we're going to change the price ID to stripe ID so this is going to correspond to a certain product instead of stripe and everything else is going to stay the exact same and the last thing we're going to do is create the images so where users can upload their own images to our products that are going to show and that are going to show on the front end in the store these product images right here this is not what users pay for okay the name is going to be images the type is going to be array because we want to allow multiple images to be passed into here as the label what users are going to see in the dashboard it's going to be product images SD Min rows we're going to set one and SD Max rows we're going to set four you could set them higher if you wanted to so users could upload more images than four in our case I think that's enough the required value is going to be set to true because we need at least one image right that I mean just imagine a product without an image no that that just doesn't make a lot of sense and as the labels we can actually customize them for example the singular is going to be image and then the plural is going to be images and this is just for the admin dashboard to get the naming just right because it tries to infer the singular and the plal by default from The Collection name and we can explicitly overwrite them to make sure it looks good in the admin dashboard like this and last thing we need to pass are the fields in here for example this takes a object with the name and this is going to be image so an array of images and each field is one image the type is going to be upload just like that the relation to is going to be to our media collection which doesn't exist yet but we're going to create it in a second where we can for example also Define the upload file size for each file and then the required value is true true because we need at least one image and that's it that's our products we can simply save that and now there's a couple things we introduced here that we don't have yet for example the media collection which is our product images let's put in parenthesis media right here this is our collection name and then the product file which is productor files as the collection name which also doesn't exist yet so we are referencing two um database tables inside of our product collection or product schema it doesn't matter it's really interchangeable um that don't exist yet right here and then up here in the product files so if we try to save this and restart the dev server we should be able to see that um oh okay we get a absolute import Arrow the at SLC config where do we use that there we go is that dot do slash do do slash there we go um there is the config awesome however if we save that again we don't even even expect this to work because we haven't referenced or we have referenced these collections but they don't exist yet so let's go ahead and create them next to the products let's create for example the media collection media. TS as a new file we don't need this to be a folder CU this collection is going to be very straightforward in what it does okay and this media collection will allow people to upload their own product images so export const media again this is going to be of type ction um config we get from payout just giving us some type help on what we can pass in here the slug for example let's set it to media and then we can do something called hooks now hooks are a concept I haven't introduced to you yet and they are really helpful in defining certain actions there's a lot of hooks we can choose from what we are going to do right here is the before change so let's implement this together and then as we do it you will see why we do it this takes an array so you can attack ATT multiple hooks basically like events right before we change this product we can invoke custom functions that we want to run so this just takes a regular function and the CMS payload also provides us data we can use to um execute these functions for example we get the request and also the actual data right so what we want to do is each product image right here should also be associated with a user and the reason we are doing this associating each product with a user directly instead of through the product so a transitive relationship um where the product images belongs to product and the product belongs to user therefore the product image belongs to the user but we can also just associate the product image directly to the user the reason is when the user is in the back end and choosing from their existing media files we don't want anyone to be able to access all the media files from other people people for example the images that they could choose from for example you as the logged in user should only be the ones that you own and to enforce that we can link the image directly to a user which is going to make this implementation much easier as you're going to see here in a second and I really mean in a second it's I mean not literally a second but very very soon in this collection right here so we can simply return the original product the dot dot dot data we can spread in the data and then attach um the user ID so the user that created this product image is going to be the rec. user. ID and that's already it just like that we Associated this image with the user that created this image and can then use it to filter and the available images here in a second in the admin panel we don't even want to show or you know what before we do that let's not implement it and then later take a look at why we are implementing that I find that to be a better approach so you can can see visually why we are going to be adding that field first off let's finish up the media so I can demo you what this even does we can give this some upload options for example the static URL and this is going to be SL media this is where we want the actual product files to live the static dur the directory is going to be under media so this is a media directory in our file system where the images will be stored now you can of course also export them to services like S3 for example ex Le AWS a bucket um which you can do in oras this is much easier much more straightforward to get started with as for the image sizes this takes an array and for example one size that we want is going to be the name thumbnail then as the width we're going to say 400 oops 400 and as the height we're going to say 300 as the position we're going to say Center and what we're doing here is generating different ver versions of these images once they are uploaded allowing us to really optimize page loading times and image sizes um in run time when users actually visit their app or app is going to be much faster because we have these optimized image versions that we can simply send to the correct screen sizes later let's copy down the thumbnail once using shift alt and arrow down change the name to card the width to 768 so the typical um break point for tablets and mobile the height is going to be 124 so24 position is going to say and center and by the way this is spelled a bit different this is Center like this I got this from the documentation I I honestly can't tell you why I always spell Center like this but they spell it like this from the CMS documentation by the way again I don't know why that's just how it is it's Center and not Center anyways let's do the last one this is going to be tablet right here the WID is going to be the 124 so the biggest one and the height we can leave this as undefined actually and by specifying undefined here or leaving the height you know unspecified um it will retain the original aspect ratio and calculate the height automatically um for larger screens which is really handy okay now as the types of um files that we want to accept here we can pass the mime types and what mime types are are for example this takes an array the image/ star so what this means is image jpack image PNG image SVG but nothing else than an image you are not going to be allowed to upload you know executable files scripts anything other but just regular images and we can make sure that rule is enforced just like this this is a really good idea for security not allowing any kind of file but just files where we know hey this is really an image as for the fields and this is going to be a very straightforward this takes an array of objects as always just like with the other collections this is only going to have one field and that is going to be the user we set up here so each image should be associated with exactly one user and we need to Define that relationship down here in the fields so the name is going to be user so the user that created this image that uploaded it the type is going to be relationship the relation to you already know the drill by now is going to be to users for um main users collection the required is going to be true we have to have a user that's why we're setting it right here in the before change so everything is um successful with the validation in the after change the has oops has many is going to be false one image belongs to one user that's it and we are not going to show this in the admin panel so we can simply as the condition return fals from the function it takes just means it won't won't show up in the actual um admin you know admin panel later on okay great let's save the media let's head over to our main payload doc config.ru media okay and I want to demonstrate you what this already looks like in the admin panel however there's one thing in the products we haven't just created yet this is the product file so what users will pay for later on so let's quickly comment this out just to take a look at what we have actually created um in the admin dashboard if everything shows up correctly if we're getting any errors let's just try it out to take a look in our dashboard let's restart the server and then navigate over to our dashboard right here and let's leave the console open just for a second so we can see if anything should go wrong let's go to our domain SL cell hit enter there we go and we can see the users the products and the media right here beautiful and now to the one field on the media where I mentioned hey I want to show you you know why we're doing this the way we are doing it and that is the hidden field so when you create a product it's totally cool to be able to upload your product images right here where we can upload media and choose from existing however right now the media also shows up as a separate category in the admin dashboard let's leave anyway go to the media and this is just a page where you can create products where you can create media just like you could create a product but that doesn't make a whole lot of sense in the products panel sure you should be able to upload images but we don't need a separate category for that in the dashboard that's a bit too much for what the media just does just you know image uploads that's all it does so therefore we can simply disable this as a separate category but still have it work just normally as we expected in the products um just that it's listed here as a separate um entity in the you know sidebar we don't want that so we can pass the admin and then the hidden is going to be a um function so this gets us access to the user and we can simply return the user. RO is not equal to admin just like that if we take a look at what else we can destructure uh it's thingss you know okay no it's it's just the user okay so we can simply make a determination if this field should be shown in the left side bar or not based on whether we are an admin or not awesome and now for the important part which is the access controls who should be able to see which kind of media now the media again it's the product images those are totally fine for everyone to see but nonetheless if you're on the back end and creating your own product you shouldn't be able to see the media that other people have uploaded into our app this is my media this is your media and I shouldn't be able to see your product images in my back end because each instance of the back end is going to be specific to that tenant to that user that is using it and I don't want to see your images right here and you don't want to see my images right here so we need to make sure that the logged in user can only see their own images in the um you know in let me show you where let's go to products um and that's going to be right here if they click choose from existing this will contain all the images of that logged in user and shouldn't be able to contain your images if I'm logged in or my images if you're logged in if that makes sense you know and to do that we are going to use the access controls on the media collection which is going to allow us to Define who can see what who can delete which images so you shouldn't be able to delete my images and I shouldn't be able to delete your images you can only delete your own and I can only delete my own of course let's start with the read aess so who can read what this is going to be an asynchronous function and we can simply return a value from here in a second first off let's destructure the request so we can make a determination if you should be able to read or not based on the request now people on the front end in or store when they're you know browsing our products everybody should see all images that's totally cool otherwise our website would be horrible cuz you know people couldn't see product images so we can make a determination based on the referrer header the cons referrer which is going to contain the URL where this request comes from is going to be the rec. headers do referrer and if we do not have a logged in user if there is no rec. user or the referrer does not include so not referrer do includes the SL SL cell or just cell which means we're on the back end then in that case we're going to return true now quick break what does this mean what did we just do essentially we're checking is the user logged in and if they are not logged in they can read all images so people browsing the front of your store should be able to read all images whereas people logged in into the actual admin dashboard they will always be logged in otherwise they couldn't even see this they should not be able to view all products so this condition would fail only in the admin dashboard same with this one if the users on the front end the URL will not contain slash cell it will only be/ cell when they're on the back end so what we're saying here is if you're on the front end and normally browsing the page you can see all images if you're on the back end or logged in on the back end you know you should not be able to see all images that's all this condition is for and then in the the other case there's a really important check we um should do so let's return a wait and then let's call a function that we call you know is admin or has access to images and we can simply invoke this now this function doesn't exist so let's go ahead and quickly create it right at the top here above our collection let's say and let actually copy this name because it's so long and turn this into an arrow function just like that okay and this Arrow function we can already give it an explicit return type and this is nothing else than the axis type we get from payload SL types so we are going to return basically an access policy that determines can you read this image or not that's all it does it's very straightforward as for the you know data that we can get in here it's another function so from this function we're going to return another function an async arrow function just like that and this async Arrow Arrow function and by the way we don't want to execute this in the code block of the is admin or has access to images but we can simply return it from that function and so from this function we are returning another function what that allows us to do is to very easily just get access to the request data that we can now use instead of The Logical block of our error function so we can do a check the cons user is going to be equal to rec. user and we can cast the type because by default let's see what it is it's any so it's not automatically inferred and so we can cast it as user which comes from at payload types remember no um absolute Imports so dot dot instead of the ad or this is going to be undefined if the user is not logged in so it's still giving us a little error here because we haven't returned anything yet and if there is no user let's return false false meaning you cannot access this image that you're requesting and true meaning yes you can access it you can read the image and that you're requesting right now if the user. roll is triple equal to admin of course they should be able to um read the image let's return true in that case admins can do everything and otherwise let's return a query constraint now what does that mean essentially we're saying um the false meaning no you cannot read this image the true meaning yes you can read this image and the query constraint let's say user as an object right here oops equals and then direct. user. ID what does that mean if we return the query constraint it means if this user owns this image if the user property of the image that we're accessing which is nothing else than this user field right here that we are setting right here when we create an image so if the image user field equals the current ly logged in user then essentially it's your image that's what it means so we are saying right here only allow access to your images if you're logged in because otherwise you shouldn't be able to see this at all or are not an admin because admins of course can see everything so otherwise just be able to see your own images that is very important to take away here that's all this is admin or has access to images does and access to image is you created it unless you're an admin right and we can now simply invoke this and pass in the request just like that and that's all we need to do for the read Access Control now the delete and update are super easy because we already have this function right here the delete just specifies who can delete which images and this is nothing else than the is admin or has access to images and invoke that and same thing for the update oops update just like that we can call the is admin or has access to images and invoke that because the request is automatically passed into that right so it's kind of like a short hand for destructuring the request and passing it in separately like this essentially it would do the same thing but so tactically it's much cleaner if we um just invoke this that's why we put it into two different functions up here so we can have an easier time down here in the um access controls really really good job that's already yeah that's the security for our media collection you can only delete your own images you can only update your own images and you can only read your own images unless people are on the front end because of course there you should be able to see all the product images otherwise it just wouldn't make sense you want people to see your own images really really cool that's our media collection and this collection allows us to do secure file uploads right here in the admin dashboard and later use them to display the products on our front end really really good job all right so easily the most important task that we have right now in building this app is actually getting the products on the front end we finally want to be able to see the products right here and you know start purchasing them so how do we do that essentially what we want our front end to look like is you know kind of something like this I know this was for a different example but we will have like four products next to each other on the homepage then have a separate page for all products and then you can also click each product and will be taken to the detail product page where for example you can add the product to the card how do we make this happen well first off let's quickly complete our um product files do you remember the product file and by the way we can close out of a lot of these right here and let's head over to our products collection because we just commented this out in order for us to check if the admin dashboard Works however let's implement this these product files right here and these are nothing else than just another collection just like all the other ones so let's go ahead and create it right here in our collections let's create a new file called produ oops product files or let's call it product file. TS because we're just going to have one in this example if you get multiple you can obviously call it product files in the plural okay as always we're going to export a con product oops product files just like that and again of type collection config we get from payload that's going to give us some intelligence on what we can pass in here once again SD what the hell is that no we want the slug in here and oh because I Mis type this this is not an arrow function now we get the type safety perfect now as the slck this is going to be productor file so the same thing we are referencing right here in our products with the relation to that's how payload or CMS knows you know that's the same collection in the admin dashboard we are actually going to hide this because of the same reason as I just explain for the media collection where there shouldn't be a separate you know uh do we have it open here a separate entry in the sidebar for the product files we don't really want that you know just as with the media right here which currently is being shown anyways let's say the hidden property which is again the function and we can destructure the user from right here and do a little conditional check if the user do rooll is not oops is not equal to admin in that case this field won't be shown in the sidebar at all so for normal users okay great now the upload property because essentially we want kind of a similar feature to the media where we can upload files but different files this shouldn't be just images but also fonts icons and you know all digital assets that you want on your platform so we need to give this a static URL where these files are going to be you know available from and this is going to be under SL product underscore files and all that means is that for example then in the end we can find these files under Local Host 3000 or or deployment version SL product files that's all it means as the static dur the directory these are going to live under product files so this directory will be available under this URL that's all we're saying here and now for the mime types what types of files do we want to accept or digital assets that users are going to pay for for example image slashstar which is a pattern any image SVG PNG jpeg all is allowed for example we can also add fonts if you wanted fonts on your digital asset platform you could easily add it just like this and then for example application SL post script and this is for you know AI files for Photoshop files for example um so people can actually upload or EPS files Vector files which are important for mockups and UI kits that's why we also accept those you don't have to you can accept totally different ones this is 100% up to you I think this is a good preset that makes sense to get started in our app as for the fields in the array that we need to pass this is only going to get one field and this is going to be the exact same as the product images so we are going to link each product file to a user so the name is going to be user the type is going to be relationship the relation to of course the is going to be a users relation to our users collection now just like with the media collection we do not want to show this in the admin dashboard this field therefore we can simply set the condition to false so normal users cannot see this field in the um admin dashboard because we don't want them to see you know the relationship it just looks weird in the dashboard the has many of course it's going to be false because one product file always belongs to exactly one user and we also require the user to be set um so we don't have like ghost product files that are not attached to any user in the end that would be pretty bad now we do have this user relation but we're not setting the user anywhere when this is created and doing that is actually really easy once again we can do that with the hooks and the hook we are going to use again just like in the media collection is the before change hook that excepts an array of hooks and let's also set a comma here for the syntax error to be gone and let's go this for example add user now this add user function that we are calling in the hook right here let's declare it right above our product files so that right before a product file is created we can make some changes to it the const add user is nothing else than a regular Arrow function we can declare right up here and this also takes a typescript type this is going to make life a bit easier for us so let's say this is a before change hook a type that we get from payload disc collections config types and not the first option for the um Imports right here so this is the correct type import let's give this a bit more space just like that and now we have Intelligence on what we can destructure in this um hook what we need is the request and also the data just like in the media collection so that we can first get the user the cons user that is creating this product file is going to be equal to the request do user something payload already gives us and once again this this is any so we can simply cast it as a user which comes from payload types remember no absolute import so let's make this relative using a dot dot and this could also be null if the user is not set now is this realistic no is it technically correct yes why isn't this realistic because well if the user can even make the request from the dashboard they have to be logged in either way we're going to enforce that in the access policies anyways nevertheless let let's return the object where we spread in all the data so everything that previously existed in this product file um that the user specified and then also add the user field which is going to be the user. ID and this needs to be optional because we set null right here and just like that whenever a product file is created we are adding this field and you know setting the correct user that is attached to that product whoever created and uploaded that file perfect now to make the um entire thing secure all right this is a very big part these product files users pay money for so we need to make very sure they don't get access by unauthorized people in this case the access policy is probably the most important across all collections because these files are worth real money so we need to get this right so the most important thing in here is by far the read property who can access a product file of course only people that that either uploaded it or that bought that file right so let's make a function out of this and call it your own and purchased so the product files that you should be able to see are either your own or the ones you um purchased on our platform so let's declare that function right up here as well cons your own and purchased what does this return it will return in Access type so an access policy just letting our CMS in typescript know hey this is what we're going to return from here and this is going to be an async arrow function okay and let's destructure right away what can we destructure well the only thing we need in here from the three we can choose from is the request right here perfect now first off by far the most important thing we need to know who is making the request so the cons user is going to be equal to the rec. user as the user type we can simply cast it in now if the user. roll if the user is an admin we we can do that check by saying the user. r is triple equal oops triple equal to admin in that case you can read everything I mean after all you own the site so we're going to return true in that case otherwise if there is no user oh by the way we can also cast this as user or null because technically I guess that is correct honestly we don't really need to do it but just to make sure and we're following good practices let's just do it if we don't have a user in that case we're going to return false you can read nothing if you're not logged in no files are accessible to you in that case and by the way we can do a question mark here because the user doesn't have to exist once we do this check for the admin okay first step when is a user allowed to read the file that they're making a request for in this function first off if you own the file so we need to get all the IDS of the products that this currently logged in user owns how do we do that very easy we can simply call the rec. payload the payload instance is actually attached to the request so we can simply await this operation and this is nothing else than the regular payload do client that we had earlier so we can simply call the find method on here search a specific collection for example our products just like that and by the way this is not recognized by typescript why well technically if we go into the payload doc config does it should be because our collection is in here but don't forget to also run the code gen in the package.json the generate types because currently if we take a look at the payload Dash types the correct type for the product is not in there because we haven't run this and generation and since creating the collection so we can simply run yarn generate um colon types and that's going to call our package.json script and put all the correct typescript types from our collections right here inside of this file and that way we should get rid of this error and it's now recognized by typescript as a valid collection just like the um users and the products and the media and we want the products in this case so as for the condition um we want to filter the products to where the user that owns these products equals the user. ID so where the user field matches the currently logged in user ID and we can also insert a depth of zero for this query what does that mean essentially when we search for products each product is attached to user by an ID and if we had a depth of one it would actually fetch the entire user that is attached to this product but we only care about the ID it's kind of like a join in SQL but we only care about the ID so we're only going to have a depth of zero and we can get access to the results by saying the docs or by destructuring the docs and calling them for example example products because that's what they are after all we're searching in the products collection now to filter out the IDS that we want to um Grant access to from this function let's say const the own product file IDs for example are going to be the products. map and for each product that we get access to in here we can simply return the prod do product oops product uncore files now it seems like typescript doesn't recognize what these are because after all we haven't inserted this collection right here into our payload doc config and the typescript files are only inferred from the collections that we passed into this array so we also need to add our product files run the Cen One More Time same command as um before the yarn generate types and oh we got an error for that um cannot read properties of undefined reading off so let's go ahead let's go back into the product file and see what could be wrong okay and I just commented out the code we are currently in the process of writing and nothing is wrong so it is that we just have unfinished code in here and so after saving all these files and finishing this code this all works as expected so let's just continue here and then we know this will work just as expected okay so for the logic in here these will give us the product files but if we take a look at the type this is a string or product file array so what we can do is simply call a flat in here just in case it also fetches the actual product files so we can make sure that this is an array of the IDS for each product file okay these are the ones you own of course you should have access to them what about the ones you bought well we also need to you know Grant the user access to those so we need to fetch all the product file IDs that this user has bought and we do it the same way as we find the products so we can copy down this query paste it right write down here in or o or after the own product file IDs and simply call it something different like the orders and of course this is not going to come from the products collection but the orders collection a collection that if you paid close attention doesn't exist yet what is an order and this collection will actually be one of the easiest ones for us to construct because an order is actually very straightforward so in order to finish up this query let's add the orders collection we can simply create a new file call it the orders dots and inside of this orders collection let's get started as always by exporting a const and let's call it for example orders because that's what our collection name will be of type collection config you know that by now and this is equal to an object the slug is going to be orders the admin we can specify some things to use just to make it look better in the dashboard for example we can say use as title this is what's going to show up in the admin dashboard your orders and then for example example as the description we can say a summary of all your orders on digital hippo period just to make very sure that everybody understands what they're looking at you know okay after this admin you know just purely decorational stuff let's add some Fields what defines an order what is an order first off there's a state an order can be paid or it cannot be paid and only if it's paid we later want to um Grant access to the actual product files when we are sure that user um sent us the money so the name is going to be underscore is paid for this field just an internal value that's why we have an underscore here it's a convention but there's nothing special about it the type is going to be checkbox so a Boolean either true or false either paid or not paid and now for the access control this is pretty important because we want to make sure no user can set their own order as paid that would be horrible for security so as for the read AIS let's say that only admins can read this by destructuring the request and then saying if the rec. user. roll is triple equal to admin only then is this going to evaluate the true and the is paid will be readable as for the create let's straight up return false you cannot create this field unless you do it programmatically through code which we are going to do and then the update also is going to be false nobody should be able to update this field unless again we do it through code so it's an or complete control in the admin panel we want to completely hide this we're going to say hidden of true so nobody can see it it should be a completely hidden internal field that users you know should not see and we also want to require this by setting the required to true because of course we either have to have a paid status or not I think that's very intuitive if you think about it okay next up we want to to have a relationship to the user who made this order and to do that again name is going to be user the type is going to be relationship you've done this a lot by now and I think it will be very clear as to why we're doing this in the admin dashboard we can simply set the hidden um to true so again this just won't show up um for the users in the dashboard we don't need them to know about this information the relation to is going to be to our collection of the users and of course we also want to set the required um value to true and we're going to do something very similar for the products relationship because if we take a look at our diagram right here an order is inherently not only linked to a user that made the order but also to a product or multiple products that are in the order so we can simply create one more object down here oops did I switch to the English keyboard I did create one more object with all the user this name is going to be the products that are contained in this order the type is going to be relationship the relation to of course you can probably guess is going to be to our products collection so we can have multiple products in one order product one product two as we laid out here for example let's require this very important of course there have to be products in an order just logically that makes sense and it can have many so let set has many to True one order can have one product or two or five or 10 doesn't really matter and that's pretty much our order collection the only thing that we want to change about this is the main collection access controls what does that mean essentially who can create orders who can read orders and we can do that by setting the axis right here on the collection level you know we did this on the field level so it's just for the isaid field and we can do the same logic for the entire collection which is really use useful for example the read who can read an order well you should only be able to read your own orders you should not be able to read my order and I shouldn't be able to see your order so let's call this your own and the logic behind this is super straightforward let's say cons your own this is going to be an arrow function that returns an access policy we get from the types just like before and we can D structure here in a second from this error function now what data do we want access to we need the request and from the request we can also just destructure the user right away so essentially we're doing the same thing as before without going the extra you know step of casting this type CU right now it's any but we don't really care we know the type so if the user. roll is triple equal to admin in that case let's return oops return true you should always be able to read this order if you're an admin you should be able to read all orders cuz literally you're an admin and otherwise we're going to return a query constraint where the user ID of the order needs to match or needs to equal right here the currently logged in user. ID just like that so you can only read your own orders that's all we're saying right here and you can't read my orders anymore okay as for the update this is very simple we can simply get the request and return If the request. user. roll is triple equal to admin so only admins can update this same exact thing by the way let's fix the syntax error for all the other crud stuff so let's copy this down once copy it down twice and just change the update to the delete and also to whatever is left that's the um create right here because we're going to do this programmatically and admins can if they want to um change them but not normal users that would be horrible and just like that that is our orders collection it's pretty straight forward if you take a look at it and now we can simply insert it right here in our config and also generate the types forward so that's all the magic there is about in order to be honest you can probably know why I'm saying this it's not that much once you do it once you know there's no black magic about it it's simply a you know table that contains a user and a product reference and that's pretty much it already um so anyways that's going to make typescript recognize the um collection for each product file so we can now finish up the access policy for each product file where you can read your own product file IDs that you uploaded to the platform and of course also the ones you bought now for the orders we're going to do a depth of two so it's going to join some tables together it's not just going to give us the order but it's also going to give us the user and not just the ID but the actual user and their products that's what the de is for we're fetching multiple levels of data instead of just the surface ID that's all it does and we are going to leave this check as it is we're only going to fetch the orders um that belong to this currently logged in user right here now to filter out the IDS let's say the cons P oops purchased product file IDs this is going to be let's map over the orders orders. map and for each order that we get we're going to execute a code block right here now the thing is if we take a look at the type this is of type order but we don't really care about the order itself we care about the product files that are in the order so we're going to return the order. products. map and for each product that is in this order we want to execute something so let's execute a code block on the product that is in here and this can be either of type string or product either the ID or the actual product now we know because of the depth that this will be the actual product but typescript doesn't it just doesn't know it you know might assume that it's the string just the ID again because of the depth we know it won't but we need to check this just for typescript so if the type of product is triple equal to string for example let's um say return rec. payload do logger do error so we're just going to log something out of the console in a kind of fancier way you could also just use console log same thing and we're going to log search depth not sufficient um to find purchased file IDs now that's you know very U very thorough error handing you could just omit this in tell typescript it's of type product um but we want to do this properly and else if this is an actual product we're going to return a Turner so if the type of product. productor files is a string in that case we're going to return the um actual product file so the product. productor files and if it's not a string in that case we know it's the entire product so this is already the ID and the other case where it's not a string it's the entire product with every field that comes with it you know like the images and the user and whatnot but we only care about the ID so let's return the product. productor files. ID because that's all we care about we want an array of IDs and at the end of the day if we take a look at the type right now this is an array array and that's not really what we want and this could also be void so what we want to do is two things first off a filter Boolean this is going to take out all undefined values um out of the array and secondly a flat which is going to turn this into a flat array so we don't have a weird array array that we can't really work with and that's the entire logic these will or these product um file IDs are now all the ones that we own that we have purchased and we can simply return a query constraint where the ID of the product files that we are requesting needs to be in an array so we can say in pass it in Array and now we want to spread in both the file IDs that we own so the dot do do own file IDs and then also the do do dot purchased product file IDs essentially constructing an array that contains both or own and the ones that we bought and we're just checking is the file that you're requesting in either of these two arrays and if it is then yes you are able to access it now what about updating and deleting and creating products well we don't need to worry about the creating part because by default everyone that is logged in can perform an action that is the default and we are fine with that default if you're logged in you should be able to upload a product file we can leave it undefined because that's going to use the default for the update we are going to allow this only for admins you know how this works by now we can destructure the request and simply return the rec. user. roll oops roll is triple equal to admin just like that the reason we're doing this is so once you upload a product file normal users are not going to be able to change it especially for um destructive actions like the let's copy this down one time separate them by comma and also in call this the delete action they shouldn't be able to delete a product file once it's on the platform because imagine you buy a product and then later on the owner of that product suddenly deletes the product file suddenly you have nothing and that's really bad so only admins can be allowed to change that and if normal users want to request a deletion they can do so through admins that you know can check has anyone ever bought this product and if they did then they can ensure that that they will remain access to that product CU otherwise this would be you know really bad if your product was just suddenly deleted from the platform um where you want to download it because you bought it right that's why we only allow this for admins awesome really really good job that actually concludes most of the work the by far largest part of the work on the application architecture in terms of data modeling and data relations and now we can actually get started in creating or first product and showing it on the front end and making people able to purchase it to actually buy the products list them on or store and by the way I just noticed we have a little typo here instant delivery you want to change that delivery there we go but just uh you know that's just a very tiny detail just uh you know saw it right here on the right hand side anyways let's actually create our first product let's start back up our development server and actually insert a product so that we have it in our data database and can then access it on the front end and show it on our page so let's head over to localhost 3000l and in here because we're logged in as an admin we can see all of these things whatever if we're logged in as a normal user we can't so we can check that let's change our own role to user hit save and then hard refresh the page and you can see the media and the product files are gone actually of course later on we will also disable that you as a user can change your Ro to add but this just makes the entire development process a bit easier but don't worry of course we're going to disable that um when we deploy this application because for security that would be really really bad just wanted to show you yes this actually works as an admin you can see them as a regular user you can't so let's create a new product let's create a product let's call it uh you know my product with any product description let's say the price is going to be $9.99 as the category let's let's say this is going to be a UI kit and as the file let's upload something for example this one right here I created in Photoshop it's called lemon milk in icon set so technically I guess you know this would be an icon set let's change that not a UI kit let's change it and now the so this is the product file the image that or the the file that people buy I'm just going to make it the same as the product image just for Simplicity sake we can choose from existing no media found okay because this is a product file but we can upload a new media filed and let's select the same thing um so we have a working product that we can now show in our store let's hit save at the very top and it's going to say product successfully created awesome let's verify that let's look in our database and see if we can find this product right here in mongodb go to products and we should be able to see there we go one of one the product has been created however it's not yet approved for sale now what does that mean if we take a look at here product status is pending verification where only admins after taking a look at the product file and seeing hey this is actually a good product can approve this to be sold on our store and we're going to later filter by the approved for sale and Boolean to only show the ones that have been actually approved that should be listed on our store beautiful so that's our product we can now show on our front end that we can now fetch from our database and show right here now where are we going to do that and the answer is we're going to create a super super useful very reusable very very practical I know I'm hyping this up but it's generally a really nice component a product re that's going to be insanely reusable across the entire application and you're going to learn a lot on how to write a proper react component um with this product re so I want to show you this let's create a new component called Product real. TSX and this is going to be super flexible and how we display data inside of it anyways let's stop hyping it up and let's actually get to the works and you're going to see why I like this so much the cons product real component is going to be again a regular plain as Arrow function where we can export default product real at the very bottom now what are we going to return from here at the very top level let's return a div and this div is going to get a class name of padding y of 12 and you know what actually let's change this to a section instead of a div just to make this in HTML semantics a bit more correct because each product reel will be its own kind of section later on inside of here goes a div with a class name of medium flex medium item Center medium justify between so this going to spread out the elements and a margin bottom of four inside of here one more div with a class name of Maximum with 2 XL a padding X of four on large a maximum width of 4 XL and on large a padding X of zero there we go perfect inside of this div we want to make this again very reusable so we can pass some things in as props into this product real for example the title that we want to display let's just mock the title for now let's say title right here so I can show you what this um component already looks like when we put it for example on the main homepage right here so that we can see in real time what we are building now this product real is going to live very close to our hero section and that's going to be right here after the button after two closing divs right here where we have the to-do of list products this is where we're going to insert the product re just like that as a self-closing component if we hit save we are able to see the product re right away beautiful right now it's just the title and wherever we call this product real we of course want to be able to change the title so inside of the product real let's receive everything as props and these props are going to be of type product real props so that we can for example change the title this um type the interface product real props is going to contain for example or title as a string and now we can go ahead and simply dynamically insert this title into our component so let's do a quick conditional check if we have a title in that case we're going to render out an H1 element there we go containing this title and this H1 is going to get a class name of text 2XL a font dasb a text Gray of 900 and on small devices and up a text of 3XL there we go currently this is going to give us a syntax error and that is we didn't destructure the title from the prop just yet so let's quickly do that destructure the title from the props and you're going to see here in a bit why we don't immediately destructure we could do that but we're not going to do it and you're going to see why in a second so let's insert the title right here and if we don't have a title in the other case we're just going to render out null now we also want access to a subtitle however we don't have to pass it so let's make it optional and this is going to be an optional string that we can also destructure the subtitle right here and and now we can copy down the entire title element and do the exact same thing for the subtitle so if we have a subtitle then we're going to render it out if we don't we're just going to render out null and of course this is not going to be inside of an H1 because that's only for headings but instead of a pag instead in a ptag a normal paragraph with a class name of margin top two a text of small and a text of muted D- foreground very nice okay and that's basically the title done when we now take a look at our page where we implemented this component we can pass a title of for example brand new where we will show the newest products that users can browse on our homepage great let's continue in the product real component so what we also want the user to be able to do is to browse the entire collection the product real is going to be very flexible so we can also for example say only show four products like we're going to do on the homepage and if they want to browse more then they will be able to click a link to get to that page so with one closing div in the closing section to go let's insert a dynamic thing right here and this is going to be an H that we're going to receive as an optional prop as well just like the subtitle so the hre is going to be an optional string if we pass it then we're going to render it out and if we don't pass it then you know no problem we're not going to render it out we can destructure it from the props and now conditionally do h check right here where if we have the HF in that case we're going to render out in xjs link component saying shop The Collection just like that and we can also put a little arrow inside of a span element this is going to get an area- hidden of true because once again this is purely decorational and we don't want this to be visible on um you know screen reader devices and in here we can insert an and a or no it's going to be an and then R ARR colon so it's like an HTML right arrow that's what it stands for r a RR right arrow and we should finish up this conditional check with rendering out null in the other case so we can already um get rid of all these syntax errors that we don't want and of course this link needs to get an H ref and this is the H ref that we pass in as a property optionally but when this link is rendered we know it will be there because of this Turner re check right here awesome now if we save that that would look fine I guess let's see it right here on the right hand side and why is this not being shown oh because we didn't pass it in from the page. TSX so for example let's link to the slash products page that's later going to contain all our products we can see shop The Collection right here but it doesn't look super good you know so let's apply some styles to this link this is going to say as the class name hidden a text of small a font D medium a text- blue- 600 a hover and then text blue 500 so it gets a little bit lighter when we hover it and on medium devices we're going to display it as block because by default it's going to be hidden okay let's save that and take a look at how this looks and it's going to be hidden in this View and once we make it a bit larger it will say shop The Collection right here really really nice so everything now is ready for us to list the actual products right here on our homepage and we're going to do that right damn now right below this link let's go to the closing div and right before the closing section let's put in a new div with a class name of relative let's open it up and in here one more div with a class name of margin top 6 a flex an items St Center and a width of full inside of here one last div with a class name of with full because we want to make sure these product are displayed very nicely and responsively this is what this differ creating right now is going to be for and with full grid grid Columns of two a gap X of four a gap y of 10 if we're in a vertical alignment on mobile devices for example let's give it a bit more space on small devices a gap X of oops a gap X of six on medium devices a grid cuse of four on medium devices a gap y of 10 and on large devices a gap X of 8 pretty long class name here it is in its entirety in case you got lost anywhere and then let's open up this div so what are we going to display in here essentially all the products that's it we want to get access to all the products that we can display and fetch from our back end and put them right in here let's move this into a split screen and display them in our product grid so the first step in achieving that is of course actually getting the products that we want to render out where do we get them from well from our database so we need to query for them right here in the product real component and we're going to do that of course in a super nice type Safe Way using trpc at the very top before the jsx return so we're going to say trpc import that dot now what are we going to use for that well the answer is it doesn't exist yet we have no way to query or products so let's create it inside of the main router inside of trpc and the logic for this is not going to be um any black magic so we can simply say for example get infinite products because we're going to do an infinite query in here to get more and more products as the user wants okay this is going to be a public procedure let's call it public procedure there we go anyone should be able to get products even logged out users and as the input we expect a z dot object in here now of course we still have to import Zod so we get X access to that utility and there's three properties we want to accept in here first off the limit how many products are we going to fetch this is going to be a z do number and the minimum value do Min is going to be one at least one product and the maximum number is going to be 100 we can simply chain it to the Z do number as separate function calls and forcing that at maximum the user can only fetch 100 products to not you know um kind of roll post or toast or database I guess as the cursor I'm going to explain it in a second what this is we're going to say Z do number this is going to be dot nullish so we don't have to pass it all the cursor is is basically the last element that was rendered so is if the user is scrolling to get more products then the cursor will be where we will begin fetching the next page of products for the infinite query and as for the actual query that we can pass in here that's going to make our product real so reusable this is going to be a custom validator so let's call it query validator so only certain options that we allow can even be passed in here this doesn't exist yet so let's create it in our source folder under the um validators let's see where is that right here in the lib folder under validators let's create a new file called query DV validator dots and the logic in here very straightforward let's export a const query validator and again this is to make sure that the user can only filter by fields that we allow to prevent abuse of our API this is going to be a z from Zod doob and inside of here let's pass three things first of the category this is going to be a oops a z do string and we can also make this optional you don't have to filter by category second the sort users should be able to sort the data this is going to be a z. enum that essentially allows us to pass an array of values that are okay um for the sord which is ASC for ascending and thec for descending order and of course you don't have to sort so we're going to make it optional and lastly the limit that we're going to pass in here this is going to be a z DOT number so 1 through 100 just like before and of course you don't have to fetch um with a limit okay and now the beauty of zodis we can actually get the typt script type just like that also let's export the type t for type query validator just so we don't get a naming conflict with the actual schema that we have right here that's why we added the T and this is going to be a z. infer where we can infer the typescript type of the type of query validator just like that a super handy Z utility we can make use of and just like that we can import the query validator right here where we get our data and all that's left now is actually the business logic of receiving the data and querying our database we're going to do that inside of a DOT query and this is nothing else than a regular you know callback function with an async before because we're going to do some await operations and we get access to the input by destructuring it right here as the first argument or the first parameter rather of the query Arrow function okay first things we need are two things from the input the query and also the cursor these are going to come as I just said from the input like that secondly we can destructure from the query so con empty object is equal to query and we know what exists that is the validator we have literally just created so let's destructure the sort the limit and then the rest but we're not going to import the category as it is because this is actually a special property I'm going to show you why here in a second so let's say dot dot dot query options and what we're doing here is getting all the other properties and calling them query options as an object so we're spreading in in the same syntax but you know to be honest it's just all the other properties except these ones from the D structure and I'm going to show you why here in a second so essentially what we want to do is just get products from our database and that in itself is very straightforward let's get our client cons payloads equal to wait get payload client so we can interact with the database and remember no absolute um Imports so we need to make this relative with dot dot and oops and also here do do get payload uh so we have relative Imports and you know getting the products as I said from the database pretty straightforward stuff we know how to do this the docks we can destructure are equal to wait payload do find so or database client or CMS and we're going to find the documents where the collection is products where the approved for sale is true so that's really important we can have a query constraint in here where the approved for sale property as we remember is very important because we don't want to sell bad products that haven't been approved by admins where the approved for sale equals approved just like that so we only list products we in admin verified Yep this is good and we can simply sort this by the you know thing that we destructure from the query then we can pass the the depth oops depth of one just like that so we fetch one level deep we can limit the sarey to the Limit that is passed into here or possibly overridden by our custom query and lastly we can pass the page in here now we're going to get to what the page is here in a second first off why is the query options you know why did we do it like this and the answer is to make it very easily extendable you see the sort and the limit both go separately right here in the query as separate parameters but all the other query options like the collection for example are actually in the wear statement so it's different from the other properties that's why we are going to do something called parsing of the um query options where we can spread in as many as we want to make it very easily extensible what does that mean let's do it and you'll see let's say the cons par query options and let's give it a type and this is going to be a record type something we get from typescript of string and then an object where we have an equals property and that is also going to be a string so we're passing a generic into the record type and by default let's initialize this as an empty object so typescript will be happy okay and now let's parse the query options where we say the object. entries and pass in the query options in our case that's just the category but as I said that makes it super easily extendable if you want to separate by you know other stuff I want to leave you with a you know really easily just an API that's very easy to extend and customize for your own ons and for each of these entries we are going to get two things but first let's initialize the code block so we can actually get a typescript you know without typescript complaining about us we can get two things in the array syntax here it's the key and the value so in our case for example if we hovered over the query object the key and the value that's all it is and we're getting access to them access to them just like this and now let's say the par query options at the index of key is going to be equal to an object where the equals oops equals is going to be the value so we're taking the raw input the category and turning it into something that or CMS understands in the query so we're putting it into a proper Syntax for our CMS that's all we're doing here where the equals is the value and just like that we are able to now simply spread in the par query options because they're now in a valid syntax just like the approved for sale so we turned a raw category with a value into this format right here that or CMS expects that's all it is that's why they're parsed very straightforward now um we still haven't def finded the page very straightforward the cons page is going to be either the cursor that we pass in or one as the default because we want to start fetching at the first item in our database that's all it is and lastly from this API Ino we still need to return the stuff to the front and to render out so we're going to return the items and we're also going to return the next page we can start fetching at and by the way both the items and the things that we're going to do right here all come from or CMS I just called the docs items because that's what they are uh you could also call them you know products I guess whatever you want let's call them items and we can also get the has next page and also the next page so if we want to implement pagination we know where to start fetching products so we're going to return the products the items and for getting the next page we can say if there is a next page if has next page in that case we're going to return the next page or else we're going to return null just like that not not is null just null and that is the logic of getting infinite products wherever we want for example in our product real right here and because we created this route we can now call it from our product real get infinite products and on here we can use the use infinite query same thing as the use Query but very easy to now fetch infinite products as the user you know Scrolls or paginates or you know basically whatever you want as the limit this takes we can simply pass in oops the limit we can actually pass has as a prop into the product real that is the main reason this is so super usable let me show you what I mean by that so the query we can receive in a prop in here is going to be of type tquery validator so everything that we have defined in our validator now let's destructure the query from the props and simply say the limit is the query. limit just like that and if that is not set cuz it's optional we can always fall back to a certain value for example let's call it an all caps the fall back underscore limit and set that at the very top of the component right here so the cons fall oops fall back underscore limit is going to be four so by default we're always going to fetch you know four products unless we specify otherwise in the query and the second thing this takes is the query we can simply pass right into here directly to our API only containing the verified fields that we allow users to set now as the second option we can pass inside of the use infinite query is the way we get the next page to actually make the infinite you know browsing of products happen and this is the get next page pram function that trpc or more like react query offers us so we can receive the last page as the first thing in the um you know Arrow function that we get here and return the last page. next page that is the method of how we fetch the next page of products and the only thing we need to do now to actually get the product is to destructure the data from this query and let's log it out just to try if everything works let's console log the data and this is going to be the data so this should load as soon as the um page is loaded we do get an error right here on the page. TSX because we didn't specify a query and now comes the beauty check this out wherever we render out the product real which we're going to do in a lot of places we can pass in the query object and simply you know adjust the query where ever we render this out for example on our main page let's sort by descending order and let's also give it a limit of four because we only want to tease some brand new products save that and let's see what happens we should see a console log but that's probably only after restarting our backend because we've done a lot of work on the index.ts and I think yeah it probably watched that and already restarted server for us and but it doesn't matter let's just restart it and see see um if we get the console log of the product data which we should so let's see is it started yes it is let's open up the console and verify that right here let's pull it up console let's refresh the page and see what happens hopefully we should see one product data in our uh console okay and we don't get a product instead we get an error and it's very apparent as to why and the reason is that the product reel of course needs to be a client side component so we need to insert a use client directive at the very top to make it a client side component and now we do there it is the data we get the page params and the pages which is because of the infinite query you know so each page has items and these items are or actual product data we get from the database and they're in this format that makes super simple to infinitely query more products as the user Scrolls for example or as they click the next page button that I'll leave totally up to you if you want to do that and for us we don't need it right now but we know that this works we actually get the products so let's display them and because they're in kind of this weird paginated format we need to do some pre-processing on them so for example the products cons products are going to be equal to the query results so let's just call the data we get the query results because the data is kind of you know it doesn't really mean anything I guess let's you know just change the name so it's a bit more clear and this is optional and then dot pages and for each page we're going to do a flat map which is basically a m a a map and a flat at the same time just in one function it's just a little convenience and for each page that we get we're going to return the page do items we have on that page oh and this needs to be items and not item there we go we can get rid of this console log and instead let's determine which products do we want to map over so let's say let map and this is going to be of type either a product which we get from payload types or this is going to be null and we will have an array of those and let's initialize this as an Mt array so all we're doing here is determining if we have products if products and and products. length which just means we have at least one product in that case we're going to map over the products to display them um right here so let's say map is going to be equal to products so we can display them otherwise if there is a loading state and we don't yet have products in that case we're going to handle that in an else statement else if the is loading and we can simply destructure the is loading state from or infinite query right up here if we are in the loading state in that case let's map over some empty boxes to beautifully show a skeleton loading State instead of the products that will stream in as we get them that's what we're doing right here so the L if is loading oops let's set the map to a new array and we we can tell typescript this is going to be a null array cuz we don't really care what's inside of the array and then here let's say the query. limit or the fallback uncore limit so we will always show as many skeleton loading boxes as we will have actual products later and we're just going to fill this with null values because it doesn't really matter what's in it just the amount of boxes will count and that's it we can literally get started in showing the products now right here so um we have created this diff with the withd of full the very long grid class name and this is where we're going to display each product inside of a custom component so first off let's map oops map. map kind of weird naming we call it map whatever and for each product and for each index that we get in here we want both we're going to render out some jsx right away and this is going to be a custom component we're going to call product listing that's going to be a beautiful product you know know one single product that handles all the logic for that so it's also very reusable across the entire application that's a component that doesn't exist yet so let's go ahead and create it under components create a new file called Product oops listing. TSX now what does a product listing do essentially it's nothing more than a link to that product so let's say the const product listing is going to be an arrow function and of course X for default product listing at the very bottom of that file now of course we need to know all the product data that we want to render out for each listing so that's why we need to pass this as a property inside each product listing component let's call this the product listing props and these props are very straightforward let's define them right up here the interface product listing props and these are going to be or contain two things the product with the actual information of type product prodct that we get from our um types from our CMS or this can also be null and then the index of that product and this is going to be a number the reason for the index is we're going to have beautiful staggered loading animations Airbnb style you're going to see what that means here in a second they will be very very beautiful and I inspired them from Airbnb so you know they will look you know pretty solid if I do say so myself now to achieve these beautiful loading animations we're going to create a state inside of this product list let's call it is visible and also set is visible by convention you not the setter and the getter of that value in react State and because we use State we also need to mark this as a client side component because we don't have you know react apis like State on the server this is going to come from the use State hook and this is going to be of type Boolean we can pass that in as a generic and default it to false by the way you don't have to pass this type typescript can infer it from here but I just find it a bit nicer to have it in there okay now we can get access to both the product and the index by destructuring them from the props we have already generated or you know to find the type after all and if we don't have a product or if this product is not visible yet that's the staggered animation we're going to create here right now in that case we're going to return the beautiful loading State a product placeholder it's going to be a self-closing custom component very simple component so we can just declare it in the same file because we're not going to use it anywhere else and you're going to see why it's so simple the cons product placeholder right here is nothing else than a regular Arrow function that returns some jsx the top level a div with a class name of flex flex-all and a width of four now let's open up this div inside of here one more div with a class name of relative a BG zinc of 100 an aspect of square a width of four and over oops overflow Das hidden and a rounded DXL just like that and inside of here we could either option A you know worry about how do we do a beautiful loading state or option b much better in my opinion say npx Shad cn- UI at latest add skeleton because our UI Library provides beautiful animated um skeleton loading States out of the box so we can simply install that and use it as it is let restart back up the dev server and now we can simply import the skeleton component just like that from our UI file we can even pass this a class name for example of height full and with full that's going to make it fill out the parent element that we have defined right here and below that closing div we're going to create two or three more skeletons skeleton just like that and these are going to indicate both the product name and the product price later on each one of these is going to get a class name of Marin top four a width of 2/3 a height of four and a rounded Das large and actually I lied they're not all going to get the same class name but let's create three of them again using shift alt and arrow down to copy them that's how we do it and the second one is going to get a class name that's a bit different it's going to be margin top two a width of 16 a height of four and rounded of large and then let's copy that one down cuz that's going to be the same as the third one just the width is going to be a bit different on the third one it's going to be withth 12 I know tiny details but it's going to make it look very very nice let's just see how that looks like for now let's mock out the um state right here we're always going to return the product placeholder so let's just see you know how that looks on our page we can already import the product listing right here in our product real and then pass in the props that we need to pass for example the product and also the index is going to be the I that's the reason we got it from the mapping right here as the second uh argument parameter whatever you know what I mean we got it right here let's save that and see what happens you know brand new is our Dev server up by the way it should be so why are we not seeing the loading State we should see it ah there we go we just needed to reload the page and that was it great so when we now R out the page we can see the beautiful loading state right here for each product and once the products are back we can actually see that it Resorts from the loading State back to the actual amount of products that we get back from our back end that is super nice so we can insert back what we just cut out to the conditional product placeholder right here and actually show the you know proper product listing with the image and the price and the name and whatnot once we um get it from our back end we don't always want to show the loading state so where are we going to do that right here below the conditional check of the product placeholder so so let's say if the is visible is true and we have a product so basically the opposite case of before in that case we're going to return some jsx what are we going to return mainly a link component from next SL link that has an H of a dynamic template string leading to slash product slash and then or product. ID that's where the detail page for one product is going to live now each link is going to get a dynamic class name so we're going to use our little CN helper function for that invoke it and pass some class names namely this is going to be invisible than a height of full a width of full a cursor that oops cursor Das pointer to make it look clickable and a group slash main okay after this class name let's pass in object to apply some conditional class names as I just said and this is going to be visible an animate Das in and a fade in of five and this is only going to apply if the is visible state is set to true to achieve that beautiful staggering animation that we're going to do now to ensure that it's actually staggered instead of all at the same time we need to keep track of the delay for each product and we're going to do that using the index so the first product will be the first to show the second product the second to show and so on all delayed by a little stagger delay you know and the way we manage that is inside of a use effect in a very straightforward logic so this use effect will only um change when the index changes because that is the only outside value that we're going to use inside of the effect so we need to pass it into the dependency array let's set a timer cons timer is equal to set timeout and this takes a callback function that you know when the timeout is over executes whatever put in here in our case that's simply setting the is visible value to True after the delay that we set and to achieve that staggered animation we can multiply the index by 75 you know milliseconds you could set this to whatever to make the Stagger more or less and then to clean up after ourself to avoid memory leaks we're going to return the clear timeout and pass in that timer okay if you've ever worked with use effect before that probably makes sense to you we need to clean up up after or self after setting a timer to avoid memory leaks that could happen when this component unmounts and the timer has not yet been triggered that's all it's for awesome okay now for the actual product what do we want to show for each product mainly that's going to be a div inside of a link component with a class name of flex a flex-all and a width of full inside of this div let's create an H3 element and this is going to contain the product. name like like that this H3 is going to get a class name of margin top four a font Das medium a text of small and a text Gray of 700 okay let's give it a bit more space by the way there we go okay after the H3 Comes A P tag with a class name of margin top one text of small and a text Gray of 500 inside of here we are going to um use the category of this product however we can't really just do product. category like that technically it will work but it's going to look weird because we don't see the human form label but for example the underscore with the UI kits you know it it looks pretty bad so we don't want that but we want the human readable label and we get that from our config so if we take a look at um know the config index I believe we called it we want this label and not the value and the way we can do that is really easy we can go ahead up here and just you know search for this product so the label that we want to use the cons label is going to be equal to the oops productor categories. find so we want to find the product where the value matches what we have and we want to extract the label from it so for each um product we can destructure the value in inside of here right away and return if the value is triple equal to the product. category now that's going to find us the entire thing we don't want the entire thing instead we only want the dot label of it right here so we can use that that's going to be the beautiful human readable UI kits or icons that we care about and not the value that we use internally CU we don't really want people to see that this label we can simply insert now instead of the product. category save that and you can see the my product and icons show up right here beautiful by the way let's move this in a proper side by side again there we go and now the last oops I accidentally clicked on it didn't mean to do that but you can already see the beautiful loading States right here very very nice and the last thing is the price so we're going to insert the format price utility we have already created and simply pass in the product. price that's so nice about this utility we can always use it across the entire application and make sure the price formatting looks good that way as the class name on this paragraph let's pass a margin top one a font Das me medium like that a text of small and a text Gray of 900 so pretty damn dark awesome let's save that see what it looks like it looks really good now the only thing missing is the product image or the images and to make sure that the image looks good and we have this beautiful slider you might remember from the very beginning of the video Airbnb style that allows us to airbnb.com that allows us to you know browse these images just just like this we are going to create one custom component for that that's going to be the image slider so we have the same beautiful browsing effect as Airbnb has and we're going to you know render out the image right above all of this stuff and call it the image slider to do exactly what I just showed you from Airbnb now this image slider is a custom component so let's create it right here as a new file called image slider. TSX let's go into here and say const image slider again a regular plain Arrow function that we simply export default at the very bottom image slider there we go very nice and the only thing this image slider takes is an array of URLs so the image URLs that we simply display here it's a separation of concerns the only thing this image slider does is you know display multiple images let's call it the image slider uh props there we go slider props to tell typescript what kind of data we will will be working with here and let's define the interface the page slider props and the only thing again the URLs are going to be a string array that we will display in here and oops I called it page slider props let's call it image slider props there we go so typescript is happy now from this component let's return a div at the top level with a class name of group relative a BG background zinc of 100 an aspect Square so we have the same beautiful squares as Airbnb an overflow Das hidden and a rounded D XL to give it like a really nice smooth border radius okay let's open up this div and inside of here one more div with a class name of absolute a z index of 10 an inset of zero an opacity of zero a group Dash hover and then an opacity of 100 for the group hover not as a separate class and then a transition property essentially what this does is only if we hover over this let's go to Airbnb only if we hover over the button is actually going to show up and not all the time so it looks exactly the same as we have it on Airbnb let's keep it open just for reference okay then let's open up this div move this with a proper side by side I don't know why it keeps leaving that there we go and inside of this div we are going to create two buttons so can say button time two and hit tab that's going to you know with mt actually create two separate buttons and these buttons are going to be the left and right button to slide along for the images and that's why they're going to use some very very similar class names so we can declare them once up here and use them in both buttons for example the active Styles when a button is active what should be applied well it's the active and then scale minus and then as a custom value in these angle brackets 0.97 so it's going to be a bit smaller just the same as on Airbnb it's going to be a grid in opacity 100 a hover and then scale 105 absolute a top of 1 12 a minus translate of - y -2 so it's exactly in the center of the image then we're going to apply an aspect d square a height of eight a width of eight a z index of 50 so it's you know above all the other content we're going to say place- it- Center a rounded Das full a border of two a BG white and last one I promise a border zinc of 300 very long class name I know but that's by far the longest one for the active styles of the button and because we wrote it once we can reuse it in the other button and never have to write it again that's the beauty of the you know declaring it as a constant up here and then using it in both buttons now for the inactive Styles they're very easy the con inactive styles are just going to be a hidden and then a text Gray of 400 that's already it that's the inactive Styles so now we can use them on or buttons however before we even style these buttons how about we just display the first image first otherwise we wouldn't even be knowing what the hell we even doing here in the first place this just makes our work in a second a lot easier already having done this so this beautiful sliding functionality we don't have to implement from scratch there's a Super Battle tested and nice library or package that handles that for us and this is called yarn ad swiper beautiful that has installed the package I've used it both in freelance projects and professionally and I really like swiper because it makes this whole sliding functionality so easy for example what we can do right below these two buttons and after the closing div of it is simply render out the swiper this is going to contain all the logic of you know left and right going for the images this swiper can be um you know not self-closing we want two separate things of it because something is going to go in between that's going to be each image that we can slide along with however first the question is where do we even get the swiper from and the answer is let's go to the very top and simply import and then as a named import swiper and and then also while we're here swiper slide from swiper SL react that is where we get this from now each swiper takes um a bunch of stuff that we can pass into it for example a class name a height or full and a width of uh full as well before we worry about the styling too much though let's actually render out the images and doing that is pretty straightforward we can simply map over the URLs that we pass as a prop into this component so url. map and for each URL and index we can render out a swiper slide so the swiper slide we already imported at the very top as a component that is not going to be self-closing because in here goes a nextjs image component so an optimized version of the image we can show on our front end this image will get a fill property a loading of eager so it loads a bit you know faster or you it starts loading sooner rather it doesn't necessarily load faster the class name is going to be a minus Z of 10 a height of full a width of full an object D cover and lastly an object D Center so we make sure the image always looks good okay lastly The Source in here is going to be the URL that we're mapping over and the alt tag is going to be product image for example you can set this to whatever you want just kind of describe the image image okay the swiper slide will get a key and this key you know is going to be just the index let's just leave it as that could also be the URL assuming you have you know um each URL is unique let's just leave it at the index that's totally fine for this use case and the class name is going to be minus Z of 10 relative a height of full and a width of full as well let's see what happens before we do that though or actually let's see what happens right away let's save all of this and import the image slider in our product listing however we still need to pass the URLs into the image slider remember so how do we get the URLs the URLs if we hit control space to see what we can pass um go right here but what do we pass like the product do you know uh images does that work not really we need to extract the URLs from the images and we're going to do that right above this jsx by saying the cons valid URLs are going to be equal to and then map over the product. images do map and for each image we're going to return something but we can first destructure the image from the product image right away and then what we're going to return is well it depends if the type of image is a string oops is triple equal to string in that case we're going to return the image because it's already the URL however if it's not of typ string then it's the entire image object and we're going to return the image. URL and if we hover over this this could be a string or null or undefined array and to get rid of all these null or undefined we can say do filter by booleans awesome if we now hover over this typescript doesn't really work with that that's fine we know it will be a string array so we can pass in the valid URLs right here and the error that we get is simply a typescript thing there's multiple ways to go about this simply ignoring the error would do the job or we can also cast this into a string array just like that because we know definitely we filtered out the undefined or no values and are totally fine to just cast it okay let's hit save and see what happens do we see anything on the front end let's reload this and we should definitely start back up the dev server and then see what happens let's let this load for a second and as you can see oh invalid Source prop host name Local Host is not confin not configured under images in your next. config.js that's fine because we're pulling the images from the file system we need to authorize our local um computer to be a valid source for these images so in our next. config we can say for the images right here we can Define the remote patterns um this is how we do it in next S4 te this is the modern approach of doing things and this takes an array the all deprecated version would have been the URLs I believe I don't know where exactly it goes and but it's deprecated in next CH 14 this is the modern way where we pass an array of objects and each one taking the host name for example Local Host then also taking the path name we can just say star star in here the port this is going to be 3,000 where we are hosting nextjs and let's turn this into a string and then the protocol is going to be um HTTP without https because this is just you know local we can save that and then restart or server and now the error should be gone awesome and it is gone but we don't really see the products show up right here why is that and let's go into our image slider the thing is swiper this module that we're using actually requires its own styles that it also provides to us so it's no big issue but we simply need to import them at the very top as well for example let's import swiper oops import as a string swiper SL CSF s and let's also import um as a string again swiper SL CSS SL pagination this pagination is going to allow us to actually switch left and right between the images in the pagination module module that swiper will provide to us save that and we can already see on the right hand side here the image beautifully fades in slowly it looks really really nice with this Airbnb style animation and an amazing loading State very very nice now how do we go left and right between the images and that is exactly where this button or where these two buttons come in now in order to use these buttons correctly we need to take care of two things two states first the first state right here con swiper and set swiper is going to contain our swiper instance so this is going to be equal to use State import that from react and this is going to be null by default as the type this is important to pass now because we don't want this to remain at null but also be able to to take up the type of swiper oops swiper type like that now where does the swiper type come from we can simply import that let's say import type swiper type from swiper like that and then everything will be fine with typescript and the second thing we want to keep track of is the active Index right that like that there we go which current image are we on the first the second and so on so active index set active Index this is going to be a use State as well and by default this is going to be zero so we're on the first um index of the images array so the you know just cover image I guess that's what it is okay and based on these we can actually um work with that in the buttons but first off we actually never set the swiper State when do we do that how do we get access to the swiper instance very easy on the swiper we have a method called on swiper so when it's done initializing we can simply receive the swiper as the um parameter or argument whatever you know what I mean and then set the swiper to that swiper instance we get as easy as that that just works now there's a few tweaks we can do on the swiper for example the space between slides let's set this to 50 pixels and the slides oop slides per view let default this to one so we only have one image per slide so it doesn't look you know like completely hot garbage for example and to allow us to switch between images left and right we also need to en enable a module and that is the in an array the pagination module that is something that you know swiper already provides to us out of the box super simple all we need to do is import it at the very top and that's going to allow us to swipe left and right between images so import the pagination module from swiper SL modules already built in no no extra installation needed as easy as that awesome now let's ask ourself when should these buttons right here show up well if there's only one image none of them should show up if we hover over it for example on Airbnb if we're on the first image well the left button is not there and the right one would also not be there if there's only one image because there's nothing to slide along with and we need to keep track of which um image slide we're currently on to then activate or deactivate show or high the buttons depending on that state and the way we do that is in the last state we're going to keep track of in this component and that is going to be the slide config and then he set slide config Just the Same by convention once again and this is going to be equal to a use State and in our case this is going to be an object where we default the is beginning oops is beginning value to true and then the isend value to the active index is triple equal to the URLs do length or zero because this could not be defined um and then minus one because this is an array and we want to start one index lower so essentially we're keeping track of are we at the beginning of the slide and are we at the end of you know how many images there are which is dynamic we're calculating that right here and we can now Define the button Styles based on this slide config however there's one issue and that is these values coming from swiper are not actually reactive so we can't trust them we need to Define this logic ourself when are we at the beginning and when are we at the end and doing that is not hard but we need to remember it so we're going to do that inside of a use effect to keep track of when we are at the beginning of the slideshow for this product and when we are at the end so the swiper allows us one method that is called on and we can call this and whenever we have an event event of slide change then we're going to update or slide config based on it this gives us a callback function we can use where we can destructure the active index so that is something sliper swiper provides to us but it's not reactive so we have to keep track of it ourself so we're going to set or reactive value the state that we keep track of set active index to that active index and then also we're going to set the slide config to keep keep track of where we currently are to an object and the is beginning value is going to be equal to the active oops active index being triple equal to zero that's when we are on the first image on the product and then the Isn value is going to take up the active index is triple equal to you can probably guess the end of the array so the URLs do length or 0 minus one so if that is null or undefined it's going to resort to zero otherwise is going to use the url. length value minus one that's when we are at the end of our little swipe show and lastly for the dependency array we're going to pass into this use effect when is it going to rerun well on all the outside values that we use in here namely that's going to be the swiper and the URLs we pass in as a prop to this component and that's that's all you know it might seem a little bit complicated but essentially we're keeping track of where we are in the current image slideshow that we can style the buttons right here accordingly so let's get to it what styles do this button get first one this is going to get a class name on and of course these are going to be very Dynamic values these are going to be you know pretty Advanced custom Styles we're going to apply to these buttons right now so what do I mean by that let's invoke the CN function and by default these are always going to take the active Styles then this first button is going to take a specific you know that the second button won't get so we're going to pass it as a separate string a right of three and a transition property then in object and we are going to apply the inactive oops inactive Styles just like that by wrapping them in these angled brackets and when are we going to apply them when we are at the slide config do isend just like that and this needs to be the inactive Styles there we go only if we are at the end of the slideshow we're going to make the slide next button which this will be inactive so you can't scroll further than the end of course that makes sense we're going to apply another string that is a hover um BG primary 300 as one class so on Hover a BG primary 300 a text primary 800 and also an opacity of 100 and we're going to apply that if we are not at the end so not slide config do is end um so we're if we're not on the last image on our slideshow again as I said pretty Advanced Styles right here but I promise that's it it doesn't get more complicated than that because this button will only contain an icon namely the Chevron right icon from luced react we can simply import that pass it a class name of height four with four and text zinc 700 because it's only an icon for screen readers and accessibility best practices we're going to include an area- label this is going to be next image so people with visual impairments know what this button does if it doesn't contain any text that they could be read out loud for or with or you know any text that they the screen reader can't read Jesus Christ okay and as for the on click what should happen when you click this button this is going to do two things first off we receive the event and then in the arrow function right here we can prevent the default by calling e do prevent default behavior and then secondly the swiper which is optional it can be undefined do slide next so that's just a method a function we can call on the swiper to slide to the next slide and before we even worry about the other button let's take a look at what this does right now and by the way we can clean up our workspace by closing all the other tabs and so let's add another image to our product to actually see what happens let's go to our back end and modify that product we already have my product let's go into the images and simply add a second image let's upload some New Media hit select a file and I'm just going to um select another UI image doesn't really matter as long as it's just any other image we can see if the swiper is working or not this is like from another um UI kit um image that I created anyways let's save that product hit save updated successfully awesome navigate back to our local host and see what happens now when we hover over this image we can see the button appear if we click it there we go we see the other image pop up beautifully and a beautiful slide animation to the next image just like Airbnb same thing now for us there's only one button we can only slide to the right let's fix that let's quickly go ahead and create the other button and by the way we can simply get rid of it and copy down this button change some things about it and then that's the slide left button because it's going to be very similar only in the reverse functionality where we slide to the left so instead of the slide next this is just going to be slide Reve as you can possibly imagine you know makes sense the right three we're going to change this to a left three because it should be on the left hand side of this image and essentially for everything else we're going to do the exact opposite as well instead of the is end this is going to be the is beginning instead of the not slide config do is end this is going to be not slide config do is beginning the area label is going to be pre oops previous image not next image and the Chevron right is going to be a Chevron left so so exact same thing just the left side button right here awesome if we reload this page then we can see we can slide to the right slide to the left do the charer real smooth and it looks very very nice we have these little image previews and honestly it looks so great wow and to make this look even better we can add a tiny detail I know just a tiny detail but it's going to make it look a lot nicer and that is customize the icon that is rendered for the pagination this is an object that gets a render bullet function where we can get the you know first um argument no parameter first parameter we don't care about it so we're just going to leave it at an underscore we just care about the class name and what we're going to return from this function will actually be shown as the pagination icon in our case let's return a template string that is an HTML element of a span with a class and this class is going to be by the way not class name this is direct HTML so just a class this is going to be a rounded D4 a transition the original class name we can simply pass in here in the dynamic template string interpolation and after the quote we can close the div and also close um close you know close the span using this synex right here I guess just regular HTML close it off like that let's save that and you can see the little icons popping up down here indicating which page we are currently on oops I didn't mean to click on that now you might realize they're not in the perfect colors it doesn't look super nice it could look a bit better and let's do that again I know tiny detail but I want you to be left with like a genuinely super nice looking app so let's do this it's very simple in the global. CSS let's say swiper Das pagination D bullet and let's give it some custom styles for example the width is going to be 7 PX and let's add an important to overwrite the default Styles the height is going to be 7px also important the background is going to oops not the background color just plus the background is going to be rgba and then 255 255 255 0.2 so a you know pretty transparent nice black tone and then the scale oops the and this needs to be separated by semicolon and not a comma and then the scale is going to be 0.9 and also add an important and now if these styles are active so the dot swiper pagination bullet active oops active in that case we're going to give it a color of FFF so a plain white a background of oops uh did we did yeah comma separated it again the background is going to be white with an important and lastly the scale is going to be one oops one and also an important so only the active one is going to be a bit larger let's see how this looks like and I know again tiny detail but this looks so much cleaner I really like how this looks very very smooth nice animation also the loading state is just a very nice overall composition and that we have here for each product and we only ride it once and it's automatically applied to every product listing in or entire application awesome job so the rest of this build is going to be mostly front and base it's a lot about styling and nice animations and making sure the user experience is as good as it possibly can be for example with the product detail page we need this to look amazing we need this to perform we need this to work so let's implement the product detail page currently there's only a 404 which is horrible let's create a new folder for this product detail page called Product now we're going to turn this into a dynamic route just like before by using angled brackets and creating a new folder inside of that product folder called product ID and then closing angled brackets I'm going to show you why this is important here in a second in here let's create a new file called page. TSX so why is it important that we have these angled brackets right here well first off let's create a page the page as always nothing else than a regular react component we can export as the default at the very bottom now these angled brackets what does it mean essentially if you take a look at the URL and or structure or architecture of how we handle this it's we send the user to the SL product slash and then the product ID that's the string up here and in order to receive that product ID we don't hardcode it as the folder name but we receive it dynamically in this Dynamic route and that is as the params that we can destructure from the props now to tell typescript what type these have this is of type page props in interface that we Define ourself right above this page so the interface page props let's call it this is just going to contain the prams and these prams will be an object where the product ID is of type string now where does this product ID come from this is the same thing as what we put in the angled brackets what you put in the angled brackets doesn't matter as long as these two right here match they need to be the same because whatever we put in the angle brackets is the prop we receive dynamically on the page you can call this whatever whatever you want just make sure it's the same as in the angle brackets then and it is logically the product at so it makes a lot of sense to call it just that now inside of this page let's start by returning or Max with wrapper to make sure the layout is awesome and give it a class name of BG White NOW inside of the max with wrapper let's create a div element with a class name of BG white as well and inside of this div one more div with a class name of this is going to be MX Auto a Max width of 2XL a padding X of four a padding y of 16 and let's give this a bit more space and then on small devices a padding X of six on small devices a padding y of 24 on large devices a grid then on large devices a maximum width of 7 XL on large devices a grid calls of two on large devices a gap X of eight and lastly on large devices a padding X of eight as well pretty long class name I'm going to be real with you but that's the longest in this entire component so don't worry about it I'm going to go full screen so in casee you missed anything here it is in its entirety and the first thing that we're going to create in here are the product details what does that mean for example product name product description and so on we can even make a little comment here for ourself product details are about to follow right here now let's begin these product details by going into a split screen again saving this so we can see what we're doing here and see what we're coding out you know on the right hand side here in real time now now in here goes a div element with a class name of large maximum width of large and on large devices a self Dash end not send a self Dash end there we go if we hover over this we can see what it does it's the Align self um in the Tailwind kind of version of how we do that okay inside of here let's create an ordered list an O which which gets a class name of flex an items stash Center and a space X of two inside this ordered list the only purpose of this is to show the current path we are on on the page this is also called breadcrumbs and we can create these breadcrumbs right here in line but it's a better idea to declare them right up here in the page because that makes them super easy to extend if we ever need to these are a constant bread crumbs so we're going to put them in all caps because this never changes it's not a dynamic value and this is an array and by the way the uppercase all uppercase is just a convention for con constants that's why we follow it you don't have to it doesn't add anything in terms of syntax it's just a convention to do it that way each breadcrumbs each bread crumb gets an ID so the ID of one a name the name for example is going to be home and lastly this is going to get an AG ref when you click on the breadcrumb this should lead to slash for the homepage and then the second item in the array very similar to the first now the ID is going to be two the name is going to be products and the hre is going to be slash products so when you click the hre you're going to get redirected to the SL products and that's it we can simply Now map over these breadcrumbs and show them on our page so to do that we're going to say breadcrumbs. map and for each bread Crum and the index we are going to receive as the second parameter we are going to render out some jsx right away and that jsx is going to be an Li element you know ordered list first thing in there an Li a list element and because we're mapping we need to give this a key and this can be for example the breadcrumb dohre anything that is unique to this item that we're mapping over inside of here let's create a div element with a class name of flex an items St Center and a text of small and inside of this div let's open it up and create a nextjs link element this of course needs an hre and this is nothing else than the bread chum. hre and it's also going to get a class name this is going to be font Das medium a text of small a text- muted D foreground or you know just normal text color on the page and then on Hover we're going to give it a text Gray of 900 okay now in this link we can simply render out the breadcrumb do name and already hit save what does this look like on our page there we go home products looks awesome however something is missing there you know just something about it is not quite right and let me show you what it is it's right below the link and we are going to do a conditional check right here where if the index is not the breadcrumbs. length minus one of course because we're working with an array essentially it's saying if we're not at the last element in that case if we're not the last element then we're going to render out a little icon here and else if we are the last element we there shouldn't be an icon separating the breadcrumbs and this icon is an SVG we could easily type out ourself but there's no sense in that so I included it in the copy paste list right here as the breadcrumbs icon you can simply grab it paste the SVG in here because there's no value in typing this out yourself um we can simply copy and paste it and save this page to see a beautiful separator now separating the home from the products and again this is super extendable if you wanted to you could have any amount of breadcrumbs simply add them in here and it would just work out of the box depending on if you want to change this um after you finish with this project with me or not you definitely have the option to okay beautiful and that's it for the breadcrumbs we're already done here now after this ordered list let's create a div and this is going to get a class name of margin top 4 inside of here goes in H1 and this H1 is going to contain the product dotame like that not the product files. name but the product. name now the product right this is a product detail page so of course we need access to the product that this page is about but how do we get that product after all the only thing we have right now is the product ID so we need a way to you know get the product from this product ID and we can simply do that using our um backend or CMS to query this product product so we can say cost payload is going to be equal to await get payload client invoke that and in order to be able to use a weight we need to mark this pages asynchronous with react server components of course we can do that that's totally um cool to do now and now query our backend to get this product data so the const docs we can destructure are going to come from the await payload Dot findind and what do we want to find well first off which collection do we want to query which table so to say it's the products table right here the limit is going to be one we are searching for one specific product and that is the one with the ID that is passed into this page now there's two things we want to filter by so first off we're going to insert a wear Clause what are we searching for first off we are searching for the product where the ID of the product equals oops equals the product idea that we get passed in from the perab right here so we can simply go ahead at the very top and destructure the product ID from the prams just like this so we get access to the product ID and can search by it in our database call and also don't forget we also want the approved for sale to equal um approved so we're only searching for products that are approved by admins to be sold on our platform and not just any product now let's call these stocks for example the products because that's what we're going to get back from the products collection and we can simply this is an array of products but we know we're only searching for one because of the limit so we can array the structure the first product from the array we get back from our CMS payload and this is our product now this doesn't have to be a product imagine if this doesn't find anything that's definitely the case if the ID is invalid and not in our database so if we don't have a product in that case we're going to return the not found that we get from next Das navigation a function we can call that's simply going to throw a 404 error for us and by doing that this is called a guard Clause we can assume that the product exists further down the page because if it didn't we would even get to that part of the rendering cycle right here so let's give this a lot more space go into a proper side by side once again like that beautiful and now we know the product. name right here so if we save that let's see what happen happens and we can see my product pop up right here because that's what we named our example product beautiful so we know it works but it doesn't look good so let's give this H1 a class name this is going to be a text of 3 XL a font D bolt a tracking D tight a text Gray of 900 and on small devices a text of 4XL there we go let's see that looks much better very very nice let's format this and then go down just just a bit and open up a new section right here after the closing H1 after the closing div this is where we're going to create a section this is going to get a class oops a class name of margin top four let's open it up and put a div element inside with a oops once again the class name of flex and items Das Center let's open up this div and inside of here goes A P tag and this is going to contain the product price and we're going to use our super handy format price utility to render out the product. price that we simply pass into the utility to handle the formatting for us this ptag is going to get a class name of font Das medium and also a text Gray of 900 just like that beautiful right below the speag one more div with a class name of margin Left 4 a border DL for left a text- muted D foreground a border gray of 300 and lastly a padding left of four let's open up this div and what goes in here is the label now if you remember what the label is essentially it's nothing else than we did previously it's the beautiful version of the product category so if you remember if we go into our config it's not the internal value but the label for that value and in order to obtain that we're going to do the same thing we did before the cons label is going to be equal to the product under score categories. find and for each value that we can destructure right away we're going to return if the value is triple equal to the product which is optional do category or actually it's not optional at this point in the rendering cycle we already know that it will exist and we only care about the label for that and just like that if we save that we should be able to see the price and the category of this product the icons pop up right here very very nice that looks awesome now we also want to show the product description right below here of course so let's scroll down the closing div right here and let's go down one more closing div still in the section but no more closing divs and create one more div and give it a class name of margin top four and a space y of six inside of here goes A P tag that's going to contain the product. description just like that perfect this is going to get a class name of text- base and also a text oops text- muted D foreground go ahead format it save it and see what happens any product description shows up right there beautiful that's exactly what should happen and now still in the section but no more closing divs let's go down create one last div in the section and this is going to contain a class name of margin top six a flex and an items stash Center inside of here we're going to render out a check icon we get from from Lucid react and this is going to be self- closing and also purely decorational so once again you know the drill area hidden can be true to hide it on screen readers let's give it a class name and this is going to be a height of five a width of five a flex shrink of zero and lastly a text green oops green of 500 beautiful let's save it see what happens there is the check mark now what is that for you might ask well it's to you know um visually support A P tag that's going to say eligible oops eligible for instant delivery to make sure users know of her website that they will get the product after they purchase it right away there is no long two-day shipping or something you know they will get sent it to their email right away so we're letting them know as the class name let's have margin left too to separate it from the check mark a text of small and a text of muted forou ground hit save perfect that looks really nice now where is the product image how do we render that out and the answer is we already did literally all the work to render out these product images so let's go ahead create them right here let's do a little comment for us these are going to be the product images by the way you don't you know have to copy the comments um this is just you know so we know what's going on in our jsx code let's put a div in here with a class name of margin top 10 to visually separate it on large devices a column start of two on large devices a row start of two on large devices a margin top of zero and on large devices a self SL um a self Dash Center there we go inside of this div one more div with a class name of aspect square and rounded Dash large for that beautiful border smooth radius and as as you remember we had that one reusable component called the image slider that we can simply use to render out these images as well that takes the URLs as the prop and once again we can simply copy over what we did for the valid URLs earlier because it's going to be the exact same logic we have a product and we want to extract the valid URLs from it so let's go into our product listing copy what we previously had with a valid URLs and we can simply paste it at the top of this component below the label above the label doesn't matter as long as we have the valid URLs in here we get access to them and now we can pass them as a property into our image slider if we hit save well we get an error you're importing a component that needs US state it works only in a client component let's see what's wrong let's go into the image slider and this needs to be a use client component so let's declare it as such and then the error will hopefully be gone there we go perfect let's go back in our page we can close out of the image slider and this just works it adapts to our page size it's super reusable and it's a very very nice component to beautifully display all the images in kind of a slideshow way awesome job we're making very very good progress here on the front end um after also making good progress on the back end I mean in fact we're pretty much done with that and then comes the add to cart you know section add to cart part so the at to card part there we go um so essentially I know that sound silly just the um button that allows users to add this product to their shopping card so let's create a div here with a class name of margin top 10 to visually separate it from the other stuff a large call start one on large devices a row start two on large devices a maximum width of large and lastly on large devices a self Dash start so once again if we hover over this it's just the Align self Flex start abstract it into kind of a Tailwind syntax inside of here we're going to put a div that gets nothing no class name no nothing it's just used for layouting and inside of here one more div with a class name of margin top 10 and inside of here we'll go the button that lets us add stuff to the cart let's mock it out for a second by saying add to card and while we're here already finished the rest and then revisit that part so right below here goes another div with a class name of margin top six and the text Dash Center and inside of this div one more div with a class name of group an inline Das Flex a text of small and a font Das medium just like that awesome okay in here goes a shield icon we get from lued react can be self- closing is only decorational so once again area hidden of true and let's give it a class name of margin right two a height of five width of five Flex shrink of Z Z and lastly a text Gray of 400 there we go right below this Shield icon let's create a little span element inside of here we can say 30-day return guarantee this span is going to get a class name of text muted foreground for our you know normal text and on Hover we're going to give it a text Gray of 700 there we go save that see what happens there we are 30-day return guarantee with a shield icon looks amazing now what doesn't look amazing is this add to card right here so let's revisit that how can we fix it and actually you know what before we do that let's quickly finish up this component I mean we're as good as done the only thing we need to do right here at the very bottom right before the end of the max with rapper is to show similar products and we have also already done the entire work for that you don't you know you don't think that that's true well let me show you we can simply render out our product real this super usable component I told you about so we can simply give it an atra of Slash products the query can be anything we want for example we are going to query the category for the same product. category that we have on this page and we can limit this to four how cool is this sort of API that we wrote ourself it's super nice the title we can insert a dynamic value for example as a template string that can be similar and then the label so for example similar icons similar UI kits as easy as that and then in the subtitle as you can probably tell it's all coming together now all the reusable components all the backend work is starting to really pay off right now and then in the subtitle we can say browse similar high quality and then label for example icons or UI kits just like and then for example in quotes the current product name so the product. name just like this let's save that and see what happens similar icons browse similar high quality icons just like my product and then we can see similar products of course we only have one in our store so it will show up right here but if there's a lot of products then other products are going to show up here all from the same category as we're browsing all similar to this product right here how cool is that and you can get as complex as you want with a similarity search you could even go as far as generating vectors and indexing each product on its semantic meaning and whatever whatever um you know write a really sophisticated recommendation algorithm here whatever you want this is all the logic right in place here and it just works out of the box with this product real component and I just think that's super cool okay anyways with with the button right here the add to card button this looks crap let's be real here I mean there's not even a button here so let's add that let's add a button that adds the item we are on right now to our shopping cart let's call it the add to cart button I mean very straightforward very blunt but it makes sense and that's a component that doesn't exist just yet so let's create it the add to card button. TSX right here as always Arrow function you know the drill add to card button that we export as the default export default add to card button at the very bottom of this component now what this does is essentially not a lot we're going to return actually our old button component from here so you might wonder Josh why are we wrapping this in a custom component then if we could just use this button well you're going to see that here in a second let's style this button first the size is going to be large just like that the class name we can pass in here is going to be a with of full so it will go from all the way left side all the way on the right side and in this button we're going to say add to card let's see what happens we need to import this in our page. TSX for the product deta Page hit save and let's see what happens there it is the add to card button by the way one really neat little detail we can add in here to make the user experience even better is to add a visual feedback that they product was added to the card successfully to achieve that let's turn this into a client side component at the very top so that we can make use of react States let's call this is success for example and set is success by convention I hope I spelled that right yeah seems like it and this comes from use state from react by default this is going to be false and we can also pass in the typescript generic again this is just a personal preference I like it it doesn't mean it's good or bad but uh you know it's just a personal preference now what should happen is that when we click this button it shows an added to cartex for like one to two seconds to make the user experience better and we can easily achieve that using use effect so inste of a use effect that gets for now in empty dependency array we are going to do the following we're going to set a timeout so the con timeout is going to be equal to set timeout and when this time out invokes or when it gets C what do we want to happen we're going to set the is success to false okay now you might wonder okay what does this do well first off before I can show you let's insert a 2C delay for this timeout to be called and also very important of course to clean up after ourself to remove the timeout to clear the timeout of this timeout to avoid possible memory leaks now this use effect would only get triggered on component Mount so on page render for now and let not ideal we want this to get called when is success is changed so whenever the is Success State changes this use effect should run therefore we should add it to the dependency array and now what happens is very interesting when we add an onclick to this button and this is just an inline Arrow function we can call what do we want to do we want to set the is Success value to true and now if we do a conditional check in the button text for example ex Le let's cut all the current text and say if is success is true then we're going to render out addit exclamation point and else we're going to render out the text we had before add to card check out what happens now let's save it click on the button and it says add it for exactly 2 seconds and gives the user a visual feedback that hey yes this operation was successful your product was added to the card a very nice detail that's going to make the user experience a lot better and um it's just a tiny detail that users notice you know and of course nothing is actually added to the card you know that's kind of the problem here everything looks nice and behaves perfectly but there is no cart you know how do we do that and the answer is actually pretty straightforward we're going to keep track of that inside of a custom store but not regular react context that is boring AF but instead we are going to do it the cool way the modern way and that is by creting a new hook so in our hooks folder let's create a new file called use cart. TS and as I said we're not going to use boring old react context for this we're going to use a modern State Management library and that is called yarn add tant so it's a I can't really spell on English but it's z s a n d so tant that's the German name you properly pronounce it it's my uh obligation as a German citizen to tell you how it's properly pronounced anyways let's start back up the dev server um and get started with this hook cuz the logic is actually not hard at all so from here let's export a cons that is going to be called use card now what is this hook what does it do essentially it will allow us to do three things we can add items to the card let's let's um comment that in here first off we can add items second off we can remove items and third off we can clear the card that's essentially all it does and then I guess optionally keep track of card items by the way you don't have to copy this just does this is just for us to understand what this does so adding items removing them clearing everything which is essentially just a derivate of removing all items or removing one item I guess and then keeping track of the current card items that's all it does and how do we achieve that pretty easy we can simply create our own store and that is using a function that or State Management Library provides to us so we can import the create function from suant by the way let's reload the dev window really quick so we get all the Auto Imports working because they might be useful in this component okay now for typescript to be happy with this we need to pass in the type of the store of the you know State essentially that we'll be keeping track of in this hook so let's define it the the type card state is nothing else than an object where the items are going to be of type card item and now what is a card item that's also a custom type we can simply export at the top here export type card item and this is nothing else than a product and we know the type of a product it's product from our CMS payload types that's all it is then the add item is going to be an operation where we can pass in a product which is of type product and then we return void so nothing from this function to tell typescript what it will do then the remove item is going to be almost the exact same so with shift alt and arrow down we can copy this down and rename it to remove item however instead of the Complete product we're simply adding the product ID or removing the product by the product ID and lastly the clear card is you know it doesn't need anything essentially we're just going to be clearing the entire state so we will receive nothing and return nothing it's just a you know operation that doesn't need any external values and we can pass this card State into this create function just like that if you're wondering why that's the way this um Library works I read it up in the documentation so it's not like I'm thinking of all of this but learning how to do this syntactically from their documentation as well okay inside of here we can invoke this once again again might look a bit weird for now but it will make sense here in a second and now one thing we want to do is you know regular react state is not persisted when you reload the page or card should be though we want to persist the card and there's a really interesting middleware that is called persist we can simply import from the state management Library it's provided to us out of the box which is super amazing and makes this whole thing super easy so we can import the persist middleware persist there we go from tant SL middleware that's going to save our stuff in local storage um just like that just by calling this now inside of here let's invoke it and get started in the actual um logic so the first thing right here is going to be an arrow function that returns an object again syntactically yes this looks a bit weird for now but things are going to clear up as we go right now the method we get in this aror function is the setter that we can use to update the step State and in the object we can return all the methods that we want or state to be or that we should be able to call in the state what we defined in the type up here for example the items these are going to be an empty array by default the add item now what should happen when we add an item we know that we get the product because that's what we told um typescript right here in the card state so we can get the product and simply return the state Setter and this takes a Arrow function that we can return something from now inside of here the state we can receive it and now update based on that state for example let's return an object where the items on this object are going to be everything we had previously so we're going to spread in dot dot dot the state do items so everything that we had before and now we're appending one item to this array and this is nothing else than the product and don't worry all these red Squigly lines will resolve once we're done with this it's not going to be long and I know they look ugly but they're not going to be here um in just a second as for the remove item functionality it's going to be pretty similar now that we receive the ID as we told typescript right up here for the remove item we can handle the logic so we want to update the state in the same way as before with a Setter where we receive the current state and can now update it by returning an object directly so this object is going to contain the items and these items are going to be nothing else than the current state filtered by the ID that we want to remove so the state the current state do items. filter and for each item that we receive in here we want to filter if the item dot oops the item. product. ID is not equal to the ID that we pass into the remove item function and lastly is going to be the clear card we also get typescript intelligence here and this is nothing else than an arrow function where we set the items object right here to an empty array that's all it does now why are there still all the squiggly lines Josh you might ask and the reason is that this persist middleware actually expects us to pass a configuration object so let's do it with the closing bracket right here so the entire thing is wrapped inside of the persist just like that ending here and then comes the configuration object from the persist where we need to pass two things that's going to be the name this is going to what this is going to be what our state appears like in local storage for example let's name it card storage and then we're going to pass the actual storage option and this is going to be a create Json storage we also get from t/ middleware and this simply will return local storage oops local storage just like that and by doing that we can tell tell tan that hey we want to save this in local storage as opposed to for example session storage oh and one thing we forgot that's the reason for all these red squiggly lines right here is that the items are actually not just one card item but of course an array of these items and this aror right here appears to be because yeah right here the product ID of course is not a product but the product ID is just a string and not the entire product that's the entire point of the remove card and that's all the logic for this beautiful custom hook that we can now reuse across multiple components for example or add card button so in order to use this we can simply destructure the add item function from our use card hook then we can import call and now we have access to this add item function so whenever we now click the button right here we want to add the current item the current product to our shopping cart that also means that we need access to this product in the button component so we need to pass it as a prop and be able to destructure it right here um as the properties in this component and we can even do this in line Define the type in line because it's the only thing we need to receive as the prop where the product is going to be of type Pro oops of type product we get from the payload types beautiful now add item we can simply pass in not payload but the product there we go and that's going to add the current item to our shopping cart now to check if this actually works or to see it in action weather I'm pretty sure it will work there's two things we need to do first off back in our page. TSX we need to pass the current product as a prop to our add card button and the second thing we want to do is show this in our card we already implemented the card functionality way earlier on as you might remember and now we want to show the product right in here and we wanted to revisit this um comp component and I definitely remember that we didn't forget about it so in our cart. TSX this is where the magic will happen where we haven't quite yet implemented the card logic now to get access to all the items in our cart we can simply go ahead way up here and say or destructure rather the or something from the use card hook that we just created and what are we destructuring well the items by the way let's also Mark this as a client component okay great it's already marked as that because we're using a a react hook here and that only works in client components now remember the item count that we mocked well now it's literally just the items. length how many items we have in our card the fee can stay one because we're going to leave it at that and we can also calculate the total amount of you know product prices in our card to do that let's say the card total is going to be equal to items. reduce so we can reduce an array to one single value and this gives us two things the total and also as the second one the current product now we only get the destructuring intelligence once we add the arrow but now we get it and what are we going to return from here well the total plus the current product. price so if you're not familiar with the reduce essentially we go over every item and we start at zero that's what we need to pass as the um default so we do that right here we're going to start at zero and go over for every product so in the first iteration this is going to be zero and add the product. price to zero at the second iteration this is going to be the zero plus the first product price now plus the second product price and so on essentially leaving us in the end with one single number the entire cost of the um card and where are we going to insert that well if we go down a bit we already mocked this out right here in the total we can now insert the card total plus the current fee let's save that and see if it works let's give this a tad more space go into a side by side and okay it still shows your card is empty because we're not actually rendering out anything for each card item for each product that's in our card that we have and it would probably be a good idea to go ahead and do that now where are we going to do that we're going to do it right here where we added the to-do of the card logic so we can simply go ahead and map over the items that are in our part that we already received at the top where we can say items. map and for each item we can destructure the product right away and render out some jsx right here um and what are we going to render out something called a card item and this custom react component that we haven't yet created is just going to contain some mark up logic some CSS styling or some Tailwind styling rather which you know is CSS but you get the point just mostly a styling component to make them look good in the card and because we're mapping we also need to remember to pass a key into this component and this is going to be the product. ID because we know that's going to be unique awesome we can get rid of this mocked text that we inserted here and one thing we want to do before just creating this card item is actually wrapping this entire thing in something called a scroll area that means if you have a lot of products in your card they won't just weirdly overflow the screen but you will be able to smooth scroll and see all your products just as you expect we could do that ourself or we could simply go ahead in our console and say npx shed cn- UI at latest add scroll area and that's already nicely styled and fully accessible and just works out of the box so why really bother doing that or self oh and I think it's actually separated by a hyphen so scroll Dash area I think that's what it is let's try that again it said selected components not found and okay yes it was this one separated by a little hyphen and now that should work installing scroll area and once that is done installing we don't need to change anything about it it literally just works out of the box and we can wrap this entire map thing in or scroll area just like that we can just make sure that nothing overflows in kind of a weird unexpected way and why isn't this working okay because we need to import it from our UI folder beautiful now about this card item that's going to complete our card um functionality here on the side let's go ahead and create it in our components create a new file called card item. TSX so I bet 100% you know the drill let's say cons card oops card item is going to be an arrow function that we export as the default at the bottom as always okay now inside of this component let's get started by returning a div element with a class name of space y of three and a padding y of two inside of here create one more div with a class name of flex an items St start a justify Das between and a gap of four oops just like that and lastly inside of here one more div with a class name of flex items stash Center and a space X of four now the first thing we want to do inside of this div is to show the product image and we do that in one last div I know a lot of divs but this is the last one for the image this is going to get a class name of relative so we can then later attach the next Jaz image to it an aspect Das Square a height of 16 width of 16 a minimum width of fit an overflow Das hidden and lastly a rounded property inside of the div as I just teased we can put the next image component however we're going to do a quick check beforehand so where the hell do we get the image from and well of course we need access to the product to be able to get the product image therefore we can simply call product right here and we can inline this product type because there's nothing else we receive in here of type product okay that means now we can access the product image and the product image is nothing more than the structuring the image from the product. images at the index of zero so we're just taking the first image and assigning it in memory to this image constant so that we can then or now rather um render it out in a um nexts image component however if we hover over this we can see it can be of type string or media it can be both so we need to do a conditional check if the type of image is unlike the string so it's a media type and the image. URL is defined in that case we're going to going to render out an image from next dress as I just said right here this can be self closing where the source and now we know the image itself is not the string URL but the media type that has a URL property instead of just being the URL so we can access the image. URL in this case the alt can be for example the product. name I think that's a good idea this is going to get a fill property and a class name of absolute and object Das cover awesome now in the alternative case when this is just a string what do we do well in the alternative case let's render out some jsx for example a div elopment something like a fallback because this is not the URL actually um I might have messed it up this is actually the ID of the image and to be honest this probably won't happen because the search depth at which we find these images are definitely sufficient to get the actual media element and the URL but types script doesn't really know that you could simply cast it as the media type but that wouldn't be you know the best practice probably so we're going to wrap this um or you know we're going to handle the case where the image is just the ID in a fallback scenario where you know it's still well handled inside of a div with a class name of flex a height of full an item stash Center a justifi Das Center and a background of secondary inside of this div we can render out an image icon we get from Lucid react this can be self-closing and because it's purely visual an area hidden of true you know why and the class name of this image is going to be a height of four width of four and text muted of foreground just as a nice Handler in case anything goes wrong um with the image we have always a fallback so we handle all the cases very very nicely okay let's see if this already Works let's save the card item and also import it right here in our card component now we also don't need to for or we don't what the hell we can't forget to pass the product as the prop into our card item because that's what we expect there and now let's see if this actually works let's give it all the space add my product to the card and then see what happens and our card still is empty okay wow that's that's not perfect are we doing everything right here we're getting the items from the use card we have the add to card button where we actually add the item so that should work right oh of course not because our development server is not even started okay so we did not even give ourself the chance to succeed here uh we need to start our development server and then we can check if the card stuff actually um you know shows correctly by the way while that starts up we can already um you know unlock these values I guess we can call it in display the actual values here for example instead of the hardcoded zero we can justplay the actual item count and the same for the card right here we can display the actual um you know uh still in parenthesis but we want the item count dynamically in here looks a bit weird looks as if we are destructuring anything but the parentheses are actually still part of the normal HTML here and you're going to see what it looks like here when we open up the card on the right hand side let's try it click the card there we are your card is empty and now let's try adding something add to card there we go our card updated and now there's one item showing up right here in our cart with the actual total value being calculated awesome very very nice we know it's working now and what we can now add is something like the product name and product or also by the way offer the user a way to remove the item from the card super important I mean what happens if I wanted to remove this right now there is no option and that is all handled right here in the um card item that we just created So currently we're only rendering out the image right here but we can do so much more in here so for example after this div right here the um relative aspect square whatever whatever that by the way you probably I hope you have also set the um control M shortcut that I told you about way in the beginning we're going to create the next element in this div with the flex item Center so go right before where that ends and in here we're going to create one more with a class name of flex flex-all and self- start open it up and in here a span element this is going to contain the product. name right here and let's give it a class name so it looks good of line- clamp das1 so it can take up at maximum one line and doesn't look then the text of small and a font D medium and lastly a margin bottom of one on awesome right below here one more span element that's going to contain the label just like that and this is nothing else than we already did previously the label where we get the you know human readable label from the product can simply go anywhere where we already declared this for example in the product ID page where I am right now copy it over and paste this at the top of the file into our um card item where we simply need to import the product C categories and then we're golden and all go to go to render all label right here and let's give this span a class name of line clamp one as well a text of extra small a capitalize a text muted foreground just like that perfect after this div let's save this and you know take a look at what this looks like by the way this looks damn good in my opinion let's give the user an option to remove this item we're going to do that inside of a div element with a class name of margin top 4 a text of XS for extra small and a text muted foreground inside of the Z let's create a button element and this button is going to contain two things first of an X icon from luced react to kind of indicate that hey this is a removal action and this icon is going to get a class name of um width three and height three and right after the icon let's say remove so it's clear to the user what this action is about to do now for the on click on this button let's call something from our use card hook what is that going to be you wonder of course this right here the remove item and to access that in our component we can simply destructure it from our use card hook here at the very top and get access to the remove item and now removing the item is as easy as simply calling the remove item for the current product that we pass in as a prop. ID and to make this look not completely like Dark Water let's give it a class name of flex items Das Center and gap of 0.5 to the button let's see what it looks like save it and right here it is the remove button let's try it out hit it and it's gone the item was successfully removed from our cart let's add it back go into our cart by the way of course we will also still take care of the mobile Navar we won't forget about that and you can see right now the item is back in our um cart perfect last thing let's list the price for this item along with the item we're going to do that um three closing divs down so the button one closing div down two closing divs down three closing divs down enter enter so with two more to go this is where we're going to create one more div with a class name of flex flex-all a space y of one a font D medium and inside of this div let's open it up goes a span element let's give it a class name of margin left Auto to kind of of space it as much from the left side as it possibly can a line clamp of one so it takes up at maximum one line and a text of small and this will contain nothing else than our format price where we can simply pass in the product. price super helpful utility that handles the formatting for us and now in the top right here um next to the you know title for the product on the right side we can see the product price and the uh shipping the transaction fee and the total price how awesome is that now let's quickly revisit the product detail page and take a look at what we even did let's zoom out refresh this page and oh what happened here to the image that looks weird that doesn't look like it should I'm definitely going to investigate why that is the case I bet we just placed one element in the wrong you know spot probably a very easy fix and then we get an error that the text content does not match server rendered html text content did not match server zero client one and that is actually nothing special in fact it's kind of expected because we maintain or item or or card state in local storage which is p the client site and the server doesn't have access to it what that means is that the HTML between server and client will be different on the server of course it's going to be zero because there is no local storage and on the client when we hydrate this then we will have one item in our card and to completely avoid this issue we can do a little trick and that is to to keep track of whether our component is already mounted or not and we're going to do that instead of a used state in our card that doesn't go in the cart item this will happen in or let's close out of all of these to clean up the workspace the cart. TSX component right here so let's do the trick we're going to keep track of the is mounted State and then the set is mounted in the form of react use State and by default this is going to be false again just because I personally preference it or personally like it I'm going to pass this as a Boolean generic and then once this component mounts we get notified of that inside of a use effect and can set the is mounted state to true and we only want this to ever execute once so we're going to pass an empty dependency array into this use effect right here and now we can simply do a check if this component is mounted then we're going to return all the values that are fetched from local storage for example or item if we are mounted then we're going to render the item count else let's just render null or zero I guess in that case not null let's save that and see what happens and try refreshing the page so let's reload this and let's add something to our card then reload and we can see right now the error is gone perfect it's initialized as zero you could also insert any other loading state if you wanted to and then once we are mounted the items are put in there just as we expected now why is this image right here I don't really know let me debug real quick this probably happens on the page. TSX for the um detail page for the product ID so let me see why this could be the case aha found it it's right here inside of this div if you want you can pause the video and take a look at for yourself I'm going to tell you the solution right now because instead of a row start to this needs to be a row span to let's save that take a look at our page and now everything looks just as we expected to and the image is on the same level as the rest beautiful that's out the page we can see our card items pop in this is rendered in a beautiful loading State animation we are making some insane progress here on this app this is looking amazing our card functionality Works let's go into side by side add to card we now have this item three times in the card and this is persisted throughout page reloads awesome now what happens if we click continue to check out let's see well we are redirected to a 4 or4 Page technically this is the slash card this shouldn't be 4 or4 this should be our actual card content and offer the user a way to check their products out to actually pay for their products but the page doesn't even exist yet so let's go ahead and create it in our app folder let's create a new folder called cart and you know the drill inside of here a page. TSX that's going to contain all the content for this card page now on this card page let's declare a cons and by the way this is kind of annoying me let's do this into a proper there we go side by side let's create a con page and this is nothing else you know the drill by now of course export default page at the bottom awesome return what are we going to return from here well at the top level a div that's going to get a class name of BG white awesome inside of here we're going to create one div and this is going to get a class name of MX Auto oops MX Auto there we go maximum width of uh 2XL a padding X of four a padding bottom of 24 a padding top of 16 on small devices a padding X of six on large devices a maximum width of 7 XL and on Dodge devices a padding X of eight there we go okay let's open up this div give it a bit more space there we go perfect and indis div goes in H1 saying shopping cart so let's save that take a look at what that looks like probably like dog water yeah it looks really bad and that's because we haven't styled this H1 so let's give it a class name of text 3XL there we go a font Das bolt a tracking Das tight a text Gray of 900 and lastly on small devices a text 4XL there we go okay save that that looks a bit better but it's not bold so font dashb there we go that looks better and right below this H1 create a div this is going to get a class name of margin top 12 on large devices a grid on large devices a grid calls of 12 on large devices in items D start on large devices a gap X of 12 and on extra large devices this is going to get a gap X of 16 so even a bit more inside of this div let's create one more div with a class name and this class name is going to be dynamic it's going to be different than the others so we're going to make use of our CN helper function right in here and by default we're going to apply a large call span 7 to this div and then conditionally we're going to do a check we are going to apply the styles of rounded Das large like that a border of two a border Dash dashed uh Dash there we go so border Dash dashed sounds weird but that's what it is a border zinc of 200 and lastly a padding of 12 and we are only going to apply the style if the shopping cart is empty now when do we know if the shopping cart is empty well currently we don't straight up so but it's very easy for us to gain access to that knowledge and that is because we can simply destructure here at the very top both the items and the remove item from our trusted use card hook and of course because we're making use of a hook here let's mark this as a use client component so we can use you know um react client side stuff in it by default server component that's not what we want now for this conditional style that we are applying right here we can now check we only applying this if the items. length is triple equal to zero so if we have no items in our card the length is going to be zero and these class names are going to be applied inside of this div let's open it up goes in H2 saying items in your shopping card and this H2 simply gets a class name of Sr only meaning meing this is only for screen readers so this is for accessibility purposes it's a pretty good practice so people with visual impairments know what's going on um so essentially it just hides this text but it's still readable for screen readers right below here let's do a conditional check if the items do length is triple equal to zero so if there's no items in the shopping cart let's go into a proper side by side by the way there we go if there's no items in the card then we're going to render out some jsx and well in the other case we're going to render out no so we're going to have like a special state for when your cart is empty what is that going to be in here let's put a div element give it a class name of flex a height of full a flex D call an items Das Center a justify Das Center and a space y of one open it up one more div element let's give it a class name of relative margin bottom of four height of 40 with a 40 and a text muted foreground now this div again is purely decorational it's going to look really good but it's not relevant for people with screen readers so area hidden once again set to True let's put a nextjs image right in here self closing and the source is going to be the/ hippo D mt- card.png let's give it a fill property loading of eager so it's going to start loading sooner and an ALT tag of empty shopping card hippo there we go okay let's save that and probably that's going to do nothing because there is no such image in or public directory let's validate that do we have a empty shopping cart hippo in their hippo empty card well no we oh wait actually we do oh there it is the hippo mty card.png is in here awesome so we should be able to um show that why isn't that oh and it seems like we just need to reload the page for that to show up however currently there's an error hydration failed because the initial UI does not match what was rendered on the server and my hot guess is that because we have the items from the local storage where we are pulling them from so let's try removing this just to check yep it's because of this but this won't be initial later so don't worry about it we're going to handle all of this to make the error um go away and little cut there because for it's the next day I remember we were just in the card right here and if my voice sounds like it's early that's cuz it is early um but I hope you can see pass through that and build this shopping cart now um together with me okay so I've already went ahead and started up the development server and oh what's happening here uh check the top level render call using div okay so it seems like we're mapping somewhere without giving or mapping a key let's see if we can in the product list okay interesting so let's go into the product listing and see if we can find the mapping are we mapping here anywhere is that in the image slider potentially is that product real okay so this is how I kind of go about debugging these things so we can see the main you know offender right here so we're mapping in a div um so for each element uh we are putting a div I believe and that's inside of the product listing inside of the product real and so for each product oops product real for each listing here um okay we are giving it the index the product but okay here we are not giving it a key which we need to do when we map over something so let's quickly give it the product. ID just like that okay and that's in the product real component and just like that we can get rid of that error great let's refresh or development server and hopefully yep all the errors are gone right no that's still one here what is this error wait let's restart the dev server and see if we still get that error let's refresh the page after that's loaded and see let's also simultaneously take a look at the browser console in case anything goes wrong here and yes there is still an error and let's see is it still okay it's again a mapping error so that means the only thing we forgot is simply to pass a key that's probably the easiest error to fix in the entirety of react that happens inside of the product real at line 73 as we can see in the stack trays um on the right side here in the console so 73 would be right here and it might seem oh okay so that makes sense um so the thing that we're mapping over right this is not necessarely the product but it could also be the empty array and if it is in that case all the keys right here are going to be null so it's going to be the same key and so for what we can do instead for example is pass a template string and let's say this is product and then the index interpolated dynamically into this template string and let's see if that works let's reload our page and see if the arrow is gone and it is awesome no more Arrow we are good to go there were some warnings but I think they don't really matter image with Source has fill but it's missing sizes okay that's just an optional thing you can pass into the images um if you want to I give it a size property doesn't really matter it doesn't you know compromise anything in app and everything works just as expected and is pretty performant at that because we're using the nextjs images um in our app perfect okay now with all the arrows gone let's go ahead and go back into our card page and let's also open up this card page right here on the front end so we can see what we're working in and here we get the hydration fil failed because the initial UI does not match what was rendered on the server did not expect server HTML to contain a div in div and that might be because of this section right here let's quickly remove it see if the aror still occurs and it doesn't so I was right why is there uh Prop class name did not match uh uh uh okay that's fine um and this all occurs by the way all these problems because um we are getting values from local storage because remember remember these items in our use card come from a client site only storage so the server has no way of knowing that it's there by the way we uh still want the section let's not permanently remove it this was just for debugging um so this works perfectly fine in the production version later on one hack we or one trick rather we can use to make this work right now is to Simply keep track of a mounted state is mounted and set is mounted and this is going to be equal to use State coming from react and then inside of a use effect so whenever this component does mount on the client side and we're sure of that by the way let's initialize this state as false and again because of personal preference let's pass in the generic you don't have to typescript can infert from the false um but I just like it inside of the use effect let's set the is mounted to true and also pass this an empty dependency array so this will only run once when the component is mounted and then for example before the conditional checks we can simply do a is mounted and the items length is mounted and the items length right here so before everything that comes from client side only sarch we can check if we are mounted um beforehand great and with that out of the way shouldn't we see the image for the empty shopping cart let's quickly see what is in our cart we can simply go into the application and then the local storage because this is where the cart is persisted and okay so there are items in our cart let's delete them we can simply delete the local storage entry and now we should see the card empty State show up right here perfect because after all that's just the local storage that's where the card is persistent users don't really care they won't really know and it just works really really well um assuming of course um or you know we still have to do this little trick I know not ideal but it's a very simple fix very straightforward um and you don't need to maintain it at any point um so it's a pretty good way to get around this okay now um let's make the empty State a bit more pretty so for example after the closing div where the image is contained let's open this up and put an H3 in here saying your card is empty and let's give this H3 a class name of font semi bolt semi bolt there we go and a text of 2 XL now right below this H3 let's put a P tag with a class name of text muted foreground you can probably get has as always for the text and a text- center inside of this ptag let's say whoops nothing to show here yet nothing to show here yet period and that's just going to look a lot better your card is empty whoops nothing to show here yet very very nice now right after this conditional check for the um items length so if we have an empty shopping cart let's create a unordered list a u L element and this is going to get a class name as well and this class name is not going to be static but it's going to be dynamic therefore let's call our super handy CN class name helper function and pass it an object inside of this object let's apply some class names conditionally so what are we going to apply well it's going to be a divide oops divide Dy so that's going to add some lines on the y axis you're going to see that here in a second a divide gray 200 that's the color of those lines a border dasb bottom a border DT and lastly a border gray of 200 200 okay and when are we going to apply this when we are mounted when is mounted and the items do length is larger than zero so if there is something in the shopping cart then every item will have a nice divider in between it and by the way one nice side effect of this is mounted um trick that we're doing is that the error from before with the different class name for server and client is also gone because on the server the conditional class name will not be applied because the is mounted state is true therefore the rest doesn't even evaluate so this will be false the class name won't be applied and it will only be applied on the client side exactly where we want it and expect it so that's a really nice side effect okay next up when we are mounted then we want to show all the items in our shopping cart of course let's do that when we are mounted is mounted then let's map over the items by saying items. map and for each item let's return a function so not some jsx right away like that and then we need to remember to return um jsx here later on because we're going to do some calculations here beforehand so um to get access to the product that we're mapping over we can simply destructure it from the mapping and then do our calculations for example the cons category is going to be equal to the productor categories doind and for each let's just call it C or category doesn't really matter we can check if the C do value is triple equal to the product. value oh and not the product Dov value but the product do category and the only thing we want from here is the label um so we're calling it category here we I believe called it label in other places in the application and why doesn't this break by the way oh because prettier expects a return okay so we can't code format just now um let's let's just call it label I think that's we call that's what we call it in the rest of the application this is essentially just the you know the UI kits or icons thing that we have in the other components as well and then let's get the image we can destructure it the product image from the pro oops is going to be equal there we go to the product. images at the index of zero so we're getting the first image that we want to show in the card so we don't have to worry about you know the image slider and whatnot that would be total Overkill uh for the card okay and let's return a li element because we have an unordered list we want to return a list element as the first you know kind of child as that unordered list and give it a key and this is going to be the product. ID and let's give it a class name and this is going to be Flex padding y of six and on small devices a padding y of 10 so just a tad more inside of here let's create a div with a class name of flex shrink zero open it up and one more div with a class name of relative a height of 24 and a width of 24 and in here we're going to display the image so the product image for the product that is in or card and of course um one thing we need to do before that is check this out the image can be of two types either the ID of the image if the search depth is not enough what I told you about earlier or it can be a media type so what we want containing the actual URL cuz we don't really care about the media collection ID right that doesn't really matter so in order to render out the element that we want we need to do a conditional check to exclude the string so we're sure it's of the media type before we render it out and that's pretty simple actually we can check if the type of image is not equal to string and we have an image. URL just like that and if we have both of these if both of these statements evaluated true in that case we're going to render out a image from next dress because in that case everything is totally fine we can give it a fil property a source of the image. URL which we now are sure exists let's give it an all tag of product image and lastly give it a class name there we go but already contain or already um you know finished this turn restatement um to get some syntax uh you know not highlighting but so we can format the code now let's revisit the class name of the image this is going to be a height oops a height of full there we go a width of full a rounded oops not a border a rounded DMD for medium an object D cover an object Das Center on small devices we want a height of 48 and lastly on small devices a width of 48 as well all right let's check it out but currently our card is empty so we won't even be able to see a product here by the way let's move this into a proper side by side let's go and add a product to the card let's click the you know on the only product that we have that's going to take us to the um product detail page and in here let's click add to card now it should show our card content so let's navigate over to slash card and see for ourself if that works and there's the image and the Beautiful separation between the different images that is what the Divide is for that I told you about here in the unorder list and the color is the Border grade 200 that we gave it this looks really really nice and we don't only want the image we want also the text the title the price and whatnot so let's add that um with the closing Li element to go let's create a new div with a class name of ml4 margin left a flex Flex of one flex-all a justify between there we go and on small devices a margin left of six let's open it up one more diff with a class name of relative a padding right of nine on small devices a grid on small devices a grid calls of two on small devices a gap X of six and on small devices a padding right there we go of zero perfect open it up inside of here goes one uh regular div with nothing attached to it just for layouting and in here one more div with a class name and that's going to be flex and justify between there we go let's open this up and let's put an H3 in here and this H3 is going to be with a class name of text small and this is where we're going to render out the you know big product title that's why it's an H A heading element inside of here let's create a link so we can make this clickable and in here let's dynamically insert the product dot name so when you click on this link you will be taken to the actual uh product so for the HF let's give it a oh and that was correct actually in a template string SL product slash and then the product. ID there we go and let's also give it a class name so it doesn't look like dog water this is going to be font medium then a text Gray of 700 and then on Hover we're going to give it a text Gray of 800 so just a bit darker let's save that see what looks like click it does it take us to the product yes it does beautiful there is our product title with one closing div let's go down here and open up one more div and this div is going to contain things like the category and the price so let's give it a class name of margin top one a flex and a text of small and let's open up the div inside of here let's put a P tag and save for the category and this is going to be the label as we calculated the before and as always this shouldn't be just any color but let's give it our text muted off foreground very very nice let's save it see what happens there it is beautiful and right below this P tag and the closing div let's go down and insert one more P tag and this is going to contain the price so using our format price utility helper we can simply insert the oops the product. price just like that call the format price utility function with it and also give this a class name of margin top one text of small a font Das medium to make it stick out just a bit more and a text Gray of 900 save that there is the price beautiful very very nice and that's also lastly provide the user an option to remove this item from their card very important of course and that's going to happen below the closing div right here with two more closing Dives and then the closing Li element to go inside of here let's put a div with a class name of margin top 4 four on small devices a margin top of zero on small devices a padding right of nine and a width of 20 let's open this up one more div and give this a class name of absolute oops absolute a right of zero and a top of zero as well now in this diff let's create a button but not any button but or custom UI button and inside of here let's put an icon that's going to be the X icon from Lucid react with a CL last name of height five a width five and also let's give it the area- hidden property of true because again it's purely decorational and doesn't add anything beyond that you know it's just an icon therefore because there's just an icon in the button as an accessibility best practice let's add an area label on the button to tell visually impaired people or screen readers um what the button will do because they can't know from the text in the button if there's just an icon and this is going to be remove product it's it's a really good idea to do this kind of stuff you know if if you want to put this on your portfolio employees will notice it it's a tiny detail but it's it is very important and to add accessibility to your apps as for the onclick Handler this is going to be the remove item and what do we want to remove the product. ID so when we click this button this is going to be taken out of our card and lastly as the variant for this button let's pass in the ghost so it's you know kind of a subtle button doesn't take up too much there it is little closing or little you know X icon to remove it from or card click it works beautifully so let's quickly go back and add it back there we go H just so we can see all the styles that we're applying and then let's navigate over to SL card beautiful there is our product and let's also add a little um descriptor here that it's eligible for instant delivery so three closing Dives down after the button let's insert a P tag with a class name of margin top 4 a flex a space X of two a text- small and a text Gray of 700 inside of here let's put a check this comes from Lucid react and is an icon this can be self-closing totally cool with a class name of height five a with five a flex shrink of zero there we go so it will always have the height and the width and a text green of 500 perfect right below here let's say inside for example a span element elig oops eligible for instant delivery so we can let the users know hey you know you'll get this instantly beautiful let's see what our page looks like and it looks very very nice on the right hand side there's still a big part missing though so what about all the prices What's the total um what's the transaction fee where can we check out where's the button to buy this product or these products in our card you know and the answer is well it doesn't exist yet so we need to go and create it and creating that is actually going to be pretty cool because that's going to be a you know just logically a very important part of our app the checkout process um and this is going to happen right below this ul and then one closing div down this is where we're going to create a section there we go because that's going to be the whole right side or our mobile devices the bottom side um because this is of course fully responsive um where we can check out and see the total and whatnot so let's give the section A Class name and this is going to be margin top of 16 rounded Das large a background gray of 50 to kind of separate it from the whites padding X of four padding y of six let's give it a on small devices a padding of six on large devices a call span of five then on large devices a margin top of zero and lastly on large devices a padding of eight pretty long class name I know let's open up the section in case you got lost anywhere let me go to the right hand side and that's the class name so inside of this section first thing we want to do is create an H2 element and this is going to say order summary there we go and let's give this age to a class name of text large a font oops font not kg text large there we go font Das medium and a text Gray of 900 save that see what happens there it is order summary beautiful we know it works and right below here let's open up a div element with a class name of margin top six and a space y of four open this up and inside of here let's create one more div element with a class name of flex items D Center and a justify Das between to space the elements out as much as possible now first thing we want to create in here is the subtotal for example let's put that in a P tag give it a class name of text small and a text Gray of 600 so let's say subtotal in here save that see what happens there it is subtotal very nice and of course we also need to show the subtotal now so inside one more P tag let's give it a class name of text small a font Das medium and a text Gray of 900 so it will be darker than the text and look very nice inside of this P tag let's do a check so if we are mounted in that case we can actually render out the price because again otherwise we will receive the hydration error and because the content is going to be different on server and client so if we are mounted everything is good we can show the price inside of our format price Helper and this is nothing else than the card total um which we still need to calculate but it's going to be very straightforward we even did that same thing before and in the other case we can simply insert a span element of you know for example zero or loading or whatever you want we could also insert like a little spinner if you wanted to actually I prefer that approach let's do a little spinner here I think that will look better by rendering out a loader too that's just a spinner icon give it a class name of height four with four animate Spin and text muted for around I think that's just going to look nicer let's try to render this out false this will always render out the spinner yeah that looks a lot better and while it's calculating the subtotal let's have a spinner instead of some text and now for the um card total how do we calculate that and we did the same thing I think in our code before the card total so we could simply grab that yeah in our card right here where we have the card total grab that and then copy paste it inside our you know uh card page right here here so let's grab that piece of code go to the very top of our page simply paste it in here so we don't need to worry about handling all that logic again um even if it's very straightforward it's just a reduce and then the car total will be here perfect now of course we don't want to leave this on false because it would never show the actual price but let's replace this with the is mounted as we had um before so we will actually see the price once the component is mounted and then a beautiful little loading State um well we don't have it yet okay awesome right below this diff containing the subtotal and the price let's create one more div and give it a class name of flex items Das Center a justify Das between a border DT and a border grade 200 and lastly a padding top of four let's open up this div and inside of here this is going to contain the transaction fee for example inside of a div element right here give give it a class name of flex items D Center text oops text- smm for small and a text muted of foreground open it up and for example inside of a span element let's say flat transaction fee because for a digital hippo we will always have a low flat fee very predictable pricing which makes it nice to users you know it's not percentage based on the purchasing count but it's always like you know 50 Cent 25 cent $1 whatever you know you want to put it on um exactly so for the value that this trans transaction fee has there we go um this is going to live in a div right below this and let's give it a class name of text small a font D medium and lastly a text Gray of 900 as for the value for this transaction fee this is going to be the same check as before if we are mounted in that case let's format the price of our fee now now what is the fee we can simply uh declare it at the very top of the file as well I believe we did it earlier now you could put this in like a central config or whatever if you wanted to uh in our case it's just an integer we're going to set to one very straightforward so $1 is going to be the fee and let's move this into a proper side by side by the way there we go this looks better and where were we hold your horses there we are okay the format price fee there we go and in the other case we can simply copy down the loading spinner from before from the first check format that see what it looks like beautiful $1 flat transaction fee perfect now what about the total let's render that out and we're going to do that below here with one closing div and one section to go this is where the order total will live inside of a div let's create it with a class name of flex items Das Center a justify between a border oops border DT a border oops border gray of 200 and a padding top of form so just like before all these ones we want to achieve a consistent style here first off div with the order total inside of it this div is going to receive a class name and this is going to be oops not an onclick a class name this is going to be a text base to make it like normal size and not small a font D medium and a tag gray of 900 so it's going to stick out from the rest order total going to be a bit larger bit darker um so we make sure it's easy to see and then below this order total let's create one more div and give it a class name of text base as well a font D medium and a text Gray of 900 open up this div and same check as before we can simply copy this down from the transaction fee paste it in here but instead of just the fee we now want the car card total plus the fee applied to it save that and there is our card total beautiful the subtotal for all the products the transaction fee that we are applying and then the order total very very nice let's see how this looks on nonmobile it looks beautiful it's here on the right hand side and the last thing we want is to offer the users a option to click the button to check out and that button will live right here with one closing div two closing div three closing div and then right before the end of the section basically this is where we're going to create one more div and the only reason for this div is to give it a margin top of six to you know space it out from this summary table right here and inside of here we're going to create a button saying check out okay now this button let's give it a class name that's going to be or actually let me show you why we're going to give it the class name it's uh you know very small and we wanted to span the entire width of this box so we're going to give it a class name of withth full the size let's make it a bit larger this should be the big button that we want users to press on now what should happen when we click this check out button that's a very good question and essentially what will happen is that we need to create a checkout page for the user containing the items that they have in their shopping cart so let's quickly sketch this out you click the check or actually let's put this inside of a rectangle so it's clear that this is the button that we have here on the left hand side so this is that blue checkout button we have right here pressing this button will call our backend and call a route that we're going to call create checkout session and this API will do exactly two things first off it will create the actual session create session using stripe with all the products that the user has in their card and the transaction fee applied and secondly it will create the UR or it will return the URL to that session so the session will have a URL You Know Field it's just a JavaScript object and uh create the URL and we can send the user to for that hosted checkout page and then return that URL only the URL we don't care about the rest to the front end when we receive that URL on the front end we can simply forward user to URL so we can simply put push that into their URL State and then they will be taken to the hostage checkout page that is managed for us by stripe so all the payment internals we don't need to worry about and we have a very easy very intuitive checkout flow click the button call an API route with the products of course we need to pass them in cart products in cart and we're going to do that by ID so very securely so that we can then create the checkout session with those products send back the URL and you know the user will be pushed to it where they can then enter their credit card or PayPal or whatever you want and check out using the products afterwards when they completed the checkout session then they will be sent to the um to the thank you page thank oops thank you there we go where they can download their product see their order details and they will also be sent a email containing the receipt there we go with the receipt all the products that they bought and the cost of them and they also um you know can download the assets okay this doesn't really make a lot of sense here but you get the idea right once they pay they get the receipt they get sent to the thank you page where they can download their assets that's the entire flow pretty intuitive and we done this part so next up we are going to implement this part where we send along the products by ID and then create the checkout session for the user and this is going to be a pretty cool process because it shows the entire payment flow so let's let create the router that will handle the payment logic and we're going to do that in a separate router just to kind of do some separations of concerns here this is going to live in our trpc we have the off router we have the index router and let's create a new file in here called the payment-related uh to payments which is not going to be a lot but it's nice to have it in a separate file to always know where we need to go when we need to implement something that has to do with payments let's export a Cons payment router from here and this is nothing else than the router we have used before nothing special about it whatsoever and we can already go ahead and add that in our index router so right here for the payment let's say we're going to use the payment router just like that great we can save that close out of the index file that's all we need to do there and let's get to the actual logic here so the create session API route this is going to be something called a private procedure oops procedure proed procedure there we go spelling is hard man I'm telling you okay anyways this is going to be something that not everyone can call so instead of the public procedure that we have for example in our um index file right here the one that everyone can call now we want to create one that only logged in users can call and doing that is more straightforward than you might think so here's a completely not mind- numbing explanation of what we are going to do when the request comes in from the front end right to create a session essentially the middleware middleware and this thing on the left here is so annoying dude it takes away so much screen space Jesus the request right here comes in before it will get executed on our server and create the actual session the middleware will get called and only if we want and the user is logged in then we're going to forward them to the server and if they're not logged in the middleware basically answers with go f yourself and won't let the request go through so basically saying no you can't do this if you're not logged in if you are fine we're going to create the actual session for you no problem and this middleware let's call it is off after understanding what it does and this is nothing else than the middleware we can get from cons middleware is going to be equal to T do middleware that's literally it we can just get it from there and this middleware function that we get from there is going to be asynchronous a sync because we can perform anything we want in here and essentially this is nothing else than an arrow function we can destructure two things from the context and the function that um lets us proceed to the next action if everything is fine first off const request is going to be equal to the context. rec that we get from from Express and this is nothing else than a regular um you know regular request in Express and or CMS automatically attaches the user to that request so we know it's of type payload request because the user will be in there it's not a completely regular request but you know very similar to it just with a user attached to it so we can destructure the user from the request and also cast the type because by default it will be any let's say this is going to be as then in an object there we go user and this is either an actual user you know if they are logged in or this could also be null just like that and if it is null it means the user is not logged in so if we don't have a user or there is no user. ID in that case the middleware should not proceed with the request and essentially like before um you know it should say uh go go f yourself you're not allowed to do this so we're going to throw a new error in this case or let's do a trpc error I think that's a bit better that's like a trpc um helper thing that's a bit more intuitive to use here with a code of um unauthorized for example so the user cannot proceed if they are not logged in and if they are logged in which essentially we made sure through that guard clause in that case we can simply return the next action in our case this will call the server um directly to create the session because there is no additional middleware of course we could Shain multiple this would call the mult um the the next middleware but we don't even need to do that and in the context let's now attach the user so this is a very cool thing about trpc let me show you what this does right we have the iso middleware right here let's quickly Define our export cons private procedure that we can now call just like the public procedure but to make sure people are logged in and this is going to be at. procedure. use so same Conta or same syntax as an Express to use a middleware and simply pass in or if is or middleware just like that and let me show you what this part does CU this is really cool where we attach the user to the context for the private procedure so once we have the private procedure in our payment router we can get the um user from it first off before we get to that part let's receive an input and the input we want to receive in the create session is going to be a z doob and of course we still to import Z from Zod to do this and the only thing we want in here are the product IDs as I said earlier we're going to pass them by IDs and this is nothing else than a z do array of strings so a z do string inside of the array now as for the actual Logic the mutation which is again nothing else than a regular plain Arrow function this is now the cool part of the private procedure that I wanted to tell you we can now destructure from here the context and the in input and check out what happens if we now destructure from the context let's say is equal to context and because we attached the user in our private procedure where did we Define it lot of files open here we can close out of some of them by the way and because we attached the user right here trpc knows it and gives us access to the user when we are using the private procedure in a completely type safe way as the user because we know they're not null because of the Guard Clause right here how cool is that that is really really nice and now let's also get the product IDs from the input and actually instead of a constant let's change it to a let because we're going to slightly modify them as we go down in this um API Rod logic to make sure they're in the correct format for stripe to know um or for stripe to handle them and if we don't have any product IDs so if or let's say product IDs do length is triple equal to to zero that means none were passed well in that case you shouldn't be able to create a checkout session let's throw a new trpc error and SD code we're going to say bad request um because you need to pass products in order to check out you know otherwise it doesn't really make sense awesome okay now the first thing we want to do is get the actual products from the IDS that are passed in here and the way we do that is by saying you know first getting access to our CMS client by saying cons payload is equal to await await get payload client as always and by the way once again because we are in the server site we cannot use um relative or absolute Imports rather we need to use relative ones again you might be able to fix this with weback I don't think it really matters we can just change it to a dot dot and we're all good to go the mutation needs to be asynchronous in order for us to use weight down here for the get paay client and now let's find these stripe IDs that is attached to each product CU if we take a look at our product collection for each one we have the price ID right here and we have a stripe ID and this is what's going to allow us to securely create the checkout session right damn now so we can destructure docs from the operation of awaiting payload doind so we're just querying or CMS or database where the collection is products right here and where do we want to find the products basically where the ID of the product is in an array in the product oops product IDs array that is and did I yeah just a a little typo here in the product IDs so get me all the products that are passed into this API route and get me you know all the data for them that's going to be contained in the docs as the product array and we can simply call it something different we can call it products because that's exactly what they are awesome and now in order order to create the checkout session for those products in order to even think about creating the checkout session we of course want to use stripe as I mentioned earlier but there is no stripe in our project so we need to create a little helper that lets us access stripe and doing that is very straightforward let's do that into in our source folder go down to our lip folder and create a new file in here called stripe. TS and there is a package that makes this super straightforward as I just said and that package is called yarn add stripe that let us initialize a stripe client that we can then use in our payment Rowser to create the actual checkout session great that has installed and in this file right here the stripe. yes let's now import Stripe from stripe so we can now work with it and there's a reason why we do this uppercase by the way we're going to see that in a second or not uppercase but capitalized so that we can export the lowercase from this file and not get a naming conflict so let's export cons stripe this is exactly what I meant we can now export the lowercase one and this is nothing else than a new stripe class instance instantiation where we can pass in the process.env you can see right here what we need to pass it's the API key and this is going to be the process. env. stripe secretor key which doesn't exist quite yet we're going to put it in or EnV or we can also um initialize this with an empty string of course that will fail but it will give us a error message and as the second thing we can pass in here this is nothing else than a configuration object for the API version let's enter 2023 uh yeah basically what we get type safety for it's this one right here and then for the typescript let's say true um because we all love typescript and that's good honestly I'll be real with you I'm not 100% sure what this does optionally indicate that you're using typescript this currently has no runtime effect okay well it it basically does nothing anyways let's pass it anyways and now we need to get the stripe secret key in order for us to create the checkout session right here in our API route so let's navigate over to stripe.com in our browser and getting the API key is super straightforward let's give this just a bit more space sign in I'm going to sign in with my account there we are and this is all test data because this is only the developer account and in here in the search bar we can and simply search for developers and then API keys this is where we can get our secret key from we can click the key to copy it over let's give this a less let's give this a lot less space there we go and inside of our. EnV file let's now create this key so the stripe uncore secretor key let's put in equals and then paste in the value we just got from stripe so the same um naming that we have right here in the process. EnV in the stripe .ts needs to match the stripe secret key right here so the EnV file knows this value is to reference and we can now use it in our payment router to create the actual session initializing stripe is as easy as that there's nothing more to it and let's now create the track out session finally this is going to happen inside of a TR catch block so we get some nice error handling in case anything goes wrong with stripe and the stripe error messages are genuinely really good actually so let's say const stripe session is going to be equal to await stripe. checkout and we of course need to import Stripe from the utility we have just created and again because we're on the back end let's change this to a relative path not an absolute one with the ad okay now the stripe check out let's say do sessions. create the API is you know pretty intuitive like that and inside of here we can pass you know a lot of things for example these success URL where should the uh people be sent when they successfully check out in our app and the answer is I think we sketched it out somewhere uh uh um right here to the thank you page there we go so the success URL is going to be or and then in a template string so we can interpolate the process. env. nexu server URL so be kind of our Baseline whether we are in deployment or in uh you know in production or locally it will be Local Host in production it will be your actual URL and then slash than- you question mark for a query Prem and we are going to pass in the order ID now this order ID where does it come from and essentially we need to create an order in our database to later see which items are in there and has it been paid or not beforehand and that ID is going to go right here so the const order that we're going to create is going to be a wait payload do create now in which collection do we want to create this is going to be the orders collection of course and then for the data that we want to create this um item with that's going to be some things for example the underscore is paid off false of course this should be the default unless we are 100% sure that user has paid which we're also going to do later on um that stripe will notify us us off then the products which products do we want to add to here well essentially and I switched to the English keyboard again essentially it's nothing else than the products that are passed in to this um API endpoint however we're beforehand going to do a quick filter let's say con filtered products because we only want to add those products where we are 100% sure that they have a price ID because that's what we need to successfully create the checkout session we cannot use any other products so let's say products. filter and and for each product that we get in here let's return if the Boolean and pass in the product. price ID so only if this is a valid Boolean the price ID so if it's a you know string that exists because it doesn't have to then it will be included in the filtered products that we can now simply use to create that order so we're only you know paying attention to products that have a price ID as easy as that and then lastly let's attach the user to the order and this is nothing else than the user. ID that is calling this API route just like that we have created an order in our database that we can now attach right here in the thank you so the order ID is going to be the order. ID that we can then on the thank you page check has this been paid or not later on and only if it has been then we're going to offer you the um the option to download your asset um because you paid us awesome let's move this into a better side by side there we go looks a bit better and then complete the checkout stripe session so what should happen if anything goes wrong if the user cancels in that case let's send the user to the and let's copy down the EnV name from here so we don't need to type this out again and let's send them to the SLC card so if anything goes wrong they abort or whatever they can simply try to check out once again as the payment payment method types we're going to pass two things that's going to be card and that's also going to be pay not payload PayPal there we go in case people want to pay using their PayPal account that's totally cool with us enter anything here that you want the mode is going to be payment and let's also attach some metadata to this checkout session this will later be available to us in the stripe web hook and why is there this big red squiggly line success URL does not exist in type request options uh don't worry about it for now we're going to fix it anyways let's complete the metadata so anyways what I was saying this is going to be available to us in the web hook and is super important for example we need to know the user. ID who checked out this is going to be the user. ID and we also need to know the order ID which is the order. ID so later on when we get notified by stripe that hey this user paid we know who paid and we know which order they paid for so we can unlock their items and last thing we need to pass in here are the line items so the actual products that this user is buying and the line items we are actually going to Define right above here as an array we're going to initialize them the cons lore items so same naming as in stripe right down here which are going to be of type stripe. checkout and we need to import stripe for this to work and by the way we don't need the entirety of stripe but we can only use the type so this will be completely excluded from or bundle in the build for the server here so the stripe. checkout Dot and then the session create params doline item and this is going to be an array of those so each line item and then we want multiple in this array and initialize this as an empty array the reason we're doing this is so we can push two things into it first off or transaction fee so we're going to say lore items. push and what are we going to push in here well essentially one line item we can see all the properties that we can pass into this and this will be an actual product that we need to create in our stripe dashboard and so let's go to stripe product and then create let's say create product in the search bar up here there we are product catalog create a product and this can be you know you can C call this anything you want I'm going to call this um you know transaction fee we don't need to pass pass it the description you can if you want like you know this helps us keep our uh platform alive whatever whatever and then the price that you want the transaction fee to be in my case I'm going to choose $1 us um and this is going to be a onetime payment and we can simply save this product right now if we save this this right here is very important this is the only thing we need for this product this is the API ID generated for us we can copy this to our clipboard and simply paste it right here for the line items push as the price so this is how stripe knows which product to add to the line items it's through an ID the quantity is going to be one we're only going to apply one time the transaction fee and the adjustable quantity let's set this enabled property to false so you won't be um able to uh you know purchase multiple transaction fees or add multiple transaction fees to one transaction that doesn't make any sense and we can already go ahead and insert the line items right here and let's see why this is a big ass red squiggly line oh and it's because right here we said card and not card okay so it needs to be with a d so it's the you know credit card and not the not the card we have uh in our app okay anyways just by doing that the big red Squigly line is gone awesome and now what about the actual products right we have the line items containing our transaction fee and if we try to check out at the moment push the big checkout button we would only see the transaction fees so we still need to push or actual products into the line items so we can simply say uh actually filtered products do for each and for each product that we reive in here we are going to push that into the line items so line items. push and we're going to push an object in here for the price this is going to be the product. price ID and S for the quantity this is going to be one because in or app and by the way we can tell the uh types we can tell typescript right here that we know this will exist because we filtered it that's the exact reason right here we definitely know that there is a price ID so we can tell typescript yes that's true and we're going to add one quantity because these are digital products and you know there wouldn't be any purpose in purchasing two times the same digital asset awesome now you might wonder where does this price ID actually be or where is that actually created we have it in our product as a field right the price and the stripe ID but we're not creating those anywhere and that's true currently we are not and the question now becomes when should we actually create that and the answer is very straightforward when a product is created right away we can register it with stripe through the API and get back the stripe ID it assigned to this product that we can then use later on in the checkout phase right so payload or CMS provides us a very handy utility for that and you've already worked with that before and that is the did we created in the products already I don't think we did hooks no we don't have a hook for creating a product yet so for the hooks we get notified we can execute our own code when a product is created for example the before change hook this is nothing else than in Array and that's also separ this from the fields using a comma and basically two things are going to happen before a product is inserted into our database first we need to add the user we have the user field but we're never actually setting it this is a custom function that we can declare right up here add user and this is going to be of type before change hook that payloads provide to us um right here from the collections this is where we get that type from and we can even pass it a generic of product from our payload types right here and again again no absolute Imports but relative dot dot dot dot pay types there we go okay for the logic this is going to be an asynchronous arrow function and in here we can simply receive or destructure the request and the data we are trying to create the product with right away and the cons user is going to be coming from the rec. user and the only thing we're going to do is extend the data that we're creating the product with by returning an object where we spread in all all the previous data that there is like the product name product price and so on and simply setting the user property of this product to the user that we receive. ID that's all for the ad user and now for the stripe stuff what should happen when a product is created essentially we're also going to perform an asynchronous function we can do it in line we can Define it as a separate function as well just like the ad user I prefer to do it in line right here so it's in the same place now we can receive all the ARG arguments right here in the arrow function and do a little check because there are two cases we need to handle and that is the operation if the arcs do operation is triple equal to create that means we are creating a new product and we are creating a new product in stripe as well else if the oper or the ARs do operation is update in that case of course we don't want to create a new product in stripe again because that product already exists and we only want to change it in stripe for example if the user changed the price we also need to change the price in stripe but not create an entirely new product now if the arc. operation is create we are making a new product and we can simply get the data con data is equal to ARS do data and we can cast it as a product because we know the type this will be now let's create a product in stripe which is nothing else than the transaction fee for example that we just did just in code automatically um we don't need to do this manually of course as the hook whenever a product is created const Created product is going to be equal to a weight stripe let's import that utility and again no absolute Imports but just relative by saying dot dot dot dot there we go stripe Dot and this is actually a very nice api. products. create you know very it's just syntax that makes sense honestly as the name for the strip product we're going to do the same name that the product will have in our database and this is the data. name that we get passed into here as for the default price data there are two things we want to Define first off the currency and this is going to be US dollar and the unit amount and this is going to be the price of the product in cents this is not in dollars but in cents so we can say math.round the data. price time 100 so if you enter like a 12 in the admin dashboard for the product this is going to be multiplied by 100 to get the sents for that price and basically you know not be a weird integer that stripe can't parse so essentially if you enter $ 12299 it will be 1,299 right here and stripe knows exactly what that means and the only thing we need to do now is simply return the updated product so let's say con updated and initialize this as the type of product so we get some um typescript intelligence for it we can simply spread in all the data that we previously had which in itself would be a valid product and now we can overwrite the fields we want to change that is the stripe ID that is the created product. ID and that is also the price ID which is by the way if you're wondering where's the difference stripe ID price ID the price ID is this right here remember that that's how strip knows how much a product costs internally and this is the created product. default price and we know this was successfully created so we can cast it as a string type to get rid of that typescript error and now all we need to do is to return the updated product from right here and that will be put into our database with the fields we just added and that's all it does basically when a product is created for a database we go ahead tell stripe hey we need a new product tell me what data you use internally for that product so we can then use that exact same data later at check out and it's as easy as that and for the operation update the logic is going to be pretty much the same let's copy down the entire create logic right here paste it in our update and slightly adjust it to this use case so for example instead of created product let's call it updated product and instead of creating a new product like we are right now we're going to say a wait stripe. product do update and now we need to pass the ID of the product that we do want to update this is going to be the data. stripe ID not the price but the stripe ID that you know stripe uses to identify that one product and as the configuration object we're going to pass the name of the data. name and also the default undor price and this is going to be the data. price ID and we can put an exclamation point here because we know this will exist so basically this handles the entire update case and now we can simply change the created product to the updated product and that's it that's all the logic there is to this and we're almost done this is by far the largest part the only thing we need to do now is return the URL from the session and push it into the uh front end so we can check out successfully that's all it takes so for the cons stripe session that we are creating here we are completely done right yes we are and now what we need to do is simply return return this URL to the front end to push it into the U window so let's return an object where the URL is the stripe session do obsession. URL property in the catch error case we can you know for example conso log the error so it will be easier to debug if anything should ever happen and then return an object that has the URL property where it is no so it's always return no matter what and we can check in the front end if we have it and if we do then push it save that that's our API logic done beautiful and now let's revisit our card page. TSX because this is where we need that logic whenever we click the checkout button remember so let's do it inside of our shopping card page right here let's navigate to the very top and create or call this API route so as always let's worry about the destructuring later and this is going to be trpc do payment or payment router do create oops create session do use mutation because that's the only thing that exists on it and now on success what do we want to happen well we want to push the URL so we can as the arrow function right here destructure the URL that we get right away which is either a string in the success or null in the error case and if we have a URL if URL then we're going to push that into the um you know into the browser the way we do that is by using the router from use router from next jsnation and not NEX router by the way again and simply call if URL router. push and push the URL that's going to forward us to the stripe hosted checkout page and that's all we need to worry about that's it and we can now call this mutate we can destructure from here by the way let's give it a custom name like create check out session is just a lot more descriptive than mutate and let's also right away destruct is loading St while we are here okay and we can simply Now call this function whenever we press the checkout button at the very bottom so let's add an on click Handler to this onclick and we're going to call the create checkout session function from above and pass in the product IDs just like that now where does this come from well we have all the products that are in our card right so the only thing we need to do to access all the IDs is to map over every product and simply return the ID we can say product IDs at the very top of the file is going to be equal to items from our cart do map and for each item or let's destructure actually right away for each product that we have in our cart we can return the product. ID and just like that we have an array of all the product IDs that we have in our cart that we can then use whenever we want to check out to pass it to the API to create that checkout session for us and that's the logic done we can also disable the button if we don't have any items if the items s length is triple equal to zero or if we are currently in a loading state which just means that a checkout session is currently being created for us in this API route of course in that case we shouldn't be able to click the button once again and just for a nice user experience let's also if we are in a loading State add a little spinner inside of our button so if is loading let's render out a loader two from Lucid react and let's give it a class name of WID four a height four an animate Spin and lastly a margin rate of 1.5 to kind of separate it from the text and if we are not loading we're simply not going to render out anything by saying null right in here and that should work let's try this together let's start up our server there we go and while that's loading we can go into a proper side by side and we should do two things first off delete the all product because this hook that we added to our products where the strip product is created that we need did not run for that product therefore this doesn't have a stripe ID and this won't have a price ID either but when we create a new product this hook will now run generate that data for us and when we check out it's going to be available to us so first thing that we're going to do is delete this product so navigate over to the/ sell page where we can manage all of that and let's log in head over to the products and delete this one let's give it a lot more space click delete yes we want to delete and let's create a new one let's say lemon milk just create any demo product it doesn't really matter as the price we're going to choose $12.99 uh this is going to be a UI kit let's say and let's choose any product file doesn't matter and add an image let's choose from existing and go with this image right here and because we're in admin I believe we can approve this product right away yeah we are an admin because we can see all the stuff here on the left hand side so we can approve and that will be listed in our store lemon milk $12.99 beautiful that just works we can go into a proper side by side and this also means that a stripe ID and a price ID should be generated for us so let's validate that let's head over to mongodb and look into our database to check that go to our product and see if we have those fields and we do price ID right here stripe ID right here beautiful so what that means now is let's get rid of the uh old product in our car that doesn't exist anymore and let's add the new lemon milk product back in and then try the entire checkout flow and see um if that works so let's go over to lemon milk let's add it to the card and then let's go to the /c card or just here continue to check out in our card there we go 309 9 that should work and let's hit the checkout button let's give this a lot more space so we can see if anything goes wrong which it shouldn't let's hit checkout and see okay the following field is invalid products let's see what happens let's take a look at our Network tab this what I usually do when debugging things like this let's see the following field is invalid validation error ah and some debugging later I found the issue and it lies in where we create the orders with pay so what happens is that the product these filter products are the actual entire products I found this just out by logging them out and we don't want to create this with the entire products but just the IDS so for each filtered product which is the entire thing let's map over those and for each product simply return the product. ID instead of literally the entire thing let's save that and restart or development server and see if that works now let's try this again let's give the page a lot more space go into our uh Network right here and click check out okay this seems to run at least let's see what happens it's loading State and we are forwarded perfect to the stripe checkout page where we have our product where we have the transaction fee applied right here enter some stripe credits and test details these are actually provided by stripe as long as we are in the um test mode so we can simply enter 42 42 42 and so on and this is going to simulate a successful checkout session enter anything here anything here because we're using the test card and are in test mode that works and enter any name for the card holder and click pay this will always be successful because of these card details this is just for us to see if everything works and then we should be redirected to the thank you page with the order ID that we created um in our CMS and this works beautiful we are at the thank you page with the order ID where we can now see see if the order was actually paid or not because stripe actually sends us a web hook I'm going to draw this out for you in a second that lets us know if the order has actually been paid for now this is exactly what we want this works beautifully and we should have a new order right here in our database and there it is beautiful okay so about the process and how the checkout Works under the H let's take a look at it this is going to be the checkout right here so they paid on the stripe checkout page exact what we just did and are then sent to the thank you page with the order ID attached as a query parameter now this payment made also triggers a completely separate action that is independent from the thank you page and that will be a stripe web hook so all that means is when stripe successfully received the payment it will send us a notification in or app so we can handle the case that users successfully paid we don't know that on the thank you page yet though that's why we attached the order ID these happen at the same time so when the user arrives on the thank you page we don't know if the payment was successful yet that's what the stbe we web Hook is for that action will trigger something in our server dots which we need to poll from our thank you page so on the thank you page every one or two seconds whatever you want half a second doesn't matter we need to check hey was this payment already successful or was it not and as soon as it is from our server by the way we can just query that here in the back and with the is paid property as soon as that is true then they will be able to download their assets and then we can keep polling you know uh that will be like a infinite Circle here as long as the isay is false we're going to keep polling and once it's true then we know the operation was successful and the user is able to download their files securely on the thank you page that is the architecture the payment is made stripe and thank you page at the same time and once stripe tells us hey everything was successful we keep polling until that you know database entry is there that we do on the server and then they can download their assets that's the flow it's really not too complicated and it all starts right here on the thank you page that doesn't currently exist so let's go ahead and create it we can close out of all of this just clean up the workspace a bunch and then give this a bit more space there we go and head into our app create a new folder called Thank D you and again inside of here a page. TSX awesome now inside of this thank you page let's get start oh we're already getting an error here all right with the cons thank you page and this is once again nothing else than an arrow function that we export default at the bottom thank you page perfect now this page will do a lot of logic for us that is the polling thing however at the first level we're just going to return a div or a main section for now there we go not just a regular div this is going to get a class name of relative and also on N devices a minimum height of full there we go inside of this main tag let's create a development with a class name of height 80 overflow hidden on large device we're going to give it an absolute on large devices a height of full on large devices a width of one half there we go that's the Syntax for it on large devices a padding R of four and on extra large devices a padding R of 12 just to make sure that it looks good everywhere awesome let's open this up and in here goes an image from next SL image this image is going to get a fill property the source is going to be the uh a static path under SL checkout dth than- you. jpack an image we can simply drag in from the GitHub here in a second the class name is going to be height of full width of full an object Das cover and an object D Center there we go we can already format this and lastly what we need to pass is the all tag thank you for your order awesome now this image comes straight from the GitHub repository right here just something I chose you can choose something totally different um I just found this to be kind of a cool image um so we can download that and literally just drag it over into our public folder where that should live so let's do that let's drag it over here we have the image go get that from the GitHub this is a massive image by the way it's 5,000 x 5,000 you could definitely go with something else this is a huge image I just found it look good but I didn't really bother they minifying it or anything so if that's what you want to do totally free to do that for now this will just work fine but just keep in mind yes this is a pretty massive image awesome right below that let's create a div and we can also go into a side by side to see our page while we work at it there we are okay inside of this div let's create one more div and this is going to get a class name of MX Auto a maximum width of 2 XL padding X of four padding y of 16 on small devices a padding oops a padding X of six on small devices a padding y of 24 on large devices a grid on large devices a maximum width of 7 XL on large devices a grid calls of two on large devices a gap X of eight then on large devices a padding X of eight on large devices a padding wi of 32 so quite a bit and lastly I promise on extra large devices a gap X of 24 lots of padding lots of white space to make this look good pretty long class name here it is if you got lost anywhere and inside of here we will create one more div this will get a class name of large call start 2 there we go all right in here an H1 element and this is going to say order successful I think with one L that's correct yeah and this H1 is going to get a class name of text small there we go a font medium and lastly a text blue of 600 perfect format that let's see what it looks like and interesting the image seems to kind of be over the content that's not a deal so let's just go ahead and add a hidden class to the image and then on small devices say for example block so it will be hidden or actually let's do medium block I think that should work yep there is the text order successful okay and just to make it look good in responsive as well right below the H1 let's create a P tag and this is going to say thank you or actually let's just say thanks for ordering there we go and I think yeah let's add a class name to this ptag this is going to be mt2 margin top of two text 4XL a font D bolt a tracking Das tight a text Gray of 900 and lastly on small devices a text 5xl so pretty large and I messed up during the HTML it seems like the first one should be the ptag and then of course the second one should be the H1 the H1 shouldn't be a little thing and then the ptag the huge title you know that's it should be the other way around this should be H1 this should be ptag it just makes a lot more sense semantically and it seems like I forgot that in the main project even okay now we need access to the order that we're passing as the order ID in the query parameters remember so the only question is how do we get access to that and the answer is well we already did the same thing in other pages if you remember we can destructure the search prams these are automatically passed into the Page by nextjs and call them for example page props now the interface page props we can simply Define right above the page you remember the search params were of a certain type and that is this object we had where the key whatever it is so that's why in this array syntax right here the key is a string and this um key will be of type string or string array or undefined so this is a standard you know type as I explained when we destructured that way earlier in the video um that URL query prems could have possibly and now to get the order ID from it very straightforward we can simply say const order ID is going to be equal to the search params do order ID so whatever we pass nope not like that whatever we pass in the URL right here let's go into proper side by side in right here the order ID awesome and it seems like we might want to hide the image from large devices and not just small uh or medium devices yeah that looks better all right that's exactly what it should be like perfect now we have access to the order ID and we can actually do some very very important logic this page needs to be very well protected to only allow authorized users to download content that they own so first off we need the user the user comes from the await get server site user or custom utility and we need to mark this as asynchronous in order to even get access to that and this also takes in the cookies remember these cookies we can simply import as well import cookies and these are provided to us by nextjs from the next slash headers there we are and we can simply call them right here cons next cookies is going to be equal to the cookies function we get from nextra s and simply invoke it that will give us the next cookies SD return value that we can then use to get the user in This Server site in this component and now comes the arguably even more important step is and that is getting the actual order that this page is for so we can say the const docs as always D structure those right away and call them orders these are going to come from await um payload so we still need the payload instance of course we need to we need a way to query our database so as always con payload is going to be equal to await get payload client we can just call that as it is on the server side nothing more to pass there and then await the payload doind now where do we want to find in our orders collection of course you know we're quaring this table for one certain order and we also can pass a Def and what that will do is give us you know instead of the ID and joining those together for example for the user it will give us the actual user instead of just the ID of that user so it's kind of like an SQL join as I explained earlier and we want to filter where the ID of the order equals the order ID so what we got from the search prams right here essentially what is up here in our URL bar what we're passing um into this page as a query parameter and this is an array of orders even though we're only searching for one but we can simply get the result the one order by array destructuring that order from the order so essentially just taking the first array element which will either be the order or it will be nothing so it doesn't have to exist and if there is no order if it wasn't found for example if the ID is invalid and doesn't exist in our database we can simply return a not found that we get from next SL navigation which will do nothing else than throw a 404 error meaning this page could not be found this order could not be found um so for example if I just added an extra a here at the very end you know that's a 44 we can't find this page so let's go back remove that a of course we want the proper page right here just to show you what this even does now another super important check we need to do is that the user of course is logged in and also that the user trying to access this page is the same one that bought this order you know if we didn't do this anyone could access this order you could send the link around and everybody could download the asset and in terms of security that would be really really bad first off let's find out the ID of the user that made the order so the order user ID let's call it and this is going to be equal to and a check if the type of order. user is a string then that's going to be the ID in that case we can just return the order. user because we want that ID otherwise it will be the entire user object therefore we can return the order. user which is the entire thing now do ID because if you take a look at the main type this is order but it could also be the um ID specifically for the user on that order and we can simply get the ID by doing this little turn check right here and now if the order user ID so the person that ordered this product is not equal to the logged in user which could be optional at this point do I oops idid there we go in that case this user is you know not logged in or not authorized therefore let's send them to the sign in page let's return a redirect which is something we get from next SL navigation and in here goes a template string where we redirect this user that is not authorized to slash sign-in there we go and then as the query parameter we can pass in origin for a nice user experience that when they log in they are redirected if they are authorized to this page right back here without needing to do all that themselves and this is going to be the than- page with the order ID being equal to the order order. ID so they will get sent right back here if they are authorized if they're not then of course not you know and uh with those logic check in place we can actually proceed with the main Logic for this thank you page so what happens if the order is paid if everything was successful and we are sure that we've received the money well in that let's let the user know that because it's really important so after the H1 element right here let's do a conditional check if the order doore is paid is true in that case let's return a paragraph element and in case it's false let's also return a paragraph element just saying something different so in the first case everything is well and the user has paid successfully let's give this pag a class name of margin top two a text base and a text muted oops muted foreground as always and inside of here let's say your order was processed and your assets are available to download below we and then end AOS colon or semicolon rather weave you know the safe way to do this weave sent your receed and Order details to and then their email and we're going to do a check here because again the order. user could be either the uh uh uh right here a string or the user and we need to we need it to be the actual user in order to render all their email which it will be because we have set the search dep right here we know it will be that but typescript doesn't so in order to let typescript no we're going to do a check if the type of order. user is not like a string in that case everything is good and it's the actual user object so let's return a span element in that case and otherwise we're simply going to return null from here inside of the span let's simply display the order uh do user. email just like that and let's give the span a class name of font medium and a text Gray of 900 just to make it pop a bit and we can also put a little period right below the conditional check to display it there so okay right now nothing happens because we're not signed in perfect that worked so let's sign in once again hello atj coding.com and anything one to3 I believe was the pass oh it wasn't the password what was it oh no it was the correct one I was all set up and ready to debug this but it was the one I just probably had a typo in there never mind okay thanks for ordering there we are and uh uh uh we should see your order was processed and so on if the order was successful now we can simply mock that state for now of course that will come from actual stripe so we are sure that user actually paid for this but for now in order to get the UI done we can simply set the is pay to true in our database to tell our database yes this order was actually paid and if we refresh the page we can see your order was processed everything is good and it seems like we have a missing space right here in the paragraph element to just have a bit of space between the email and the uh to Word Perfect perfect thanks for ordering really really nice now what happens if the order is not successful then let's render out a P tag with a class name of margin top 2 a text base and a text muted foreground and inside of here say we appreciate your order and we and then and AOS uh semicolon we are currently processing it to let the user know hey we're in a waiting a pending kind of state so hang tight let's format this and we oops and we and AOS semicolon wheel that's why we will send you confirmation very soon to let the user know hey everything is good we got your order and we're currently polling stripe in order to check if it was actually successfully paid or not of course we don't say literally that because the user wouldn't know what's happening but that's the idea behind this awesome let's put a div right below here with a class name of margin top 16 a text of small and a font D medium and inside of here let's put two more divs the first one with a class name of text muted foreground there we go saying order number and the second one which which is going to get a class name of margin top two and a text Gray of nine 100 and this is going to contain the actual order number or the order ID I guess which is you know it's more user friendly to say number so it's very clear what it is and there it is if you have any support requests or whatever you can always pass the ID and we as administrators know that it's the order ID in the database that we can simply look up and if anything went wrong with a payment for example we can change it manually it's just always very smart to have it as a backup in case anything goes wrong ever you know okay and then right below here let's actually list the products in a beautiful list in an unordered list that the user has bought this UL gets a class name of margin top 6 a divide Dy a divide gray 200 a border top a border gray of 200 a text of small a font Das medium and a text of muted foreground perfect let's open up this unordered list um element and inside of here map over the products that the user user has bought so let's open this up and say order do products and by the way this could theoretically be a string or product array but we just know because of the depth that we queried these um product at that this will definitely be the actual product therefore it is totally cool to cast this type as the product type we get from our CMS array because we know it will always be the type with or sufficient search depth let's map over this and for each product that we have let's return a function because we're going to do some calculations and later return some jsx right down here the only calculations we need to do are the ones we've already done before pretty much for example we need to get the label for example in the dynamic product at the page so we can simply grab that go over back to our thank you page and paste that in assuming of course we have all the Imports that we need for this second one is going to be the download URL where do we download the asset that we're mapping over right here let's define it it's pretty straightforward the cons download URL is going to be the product. product files that are attached to this product so what the user actually pays for as we discussed earlier as product file once again we're casting this because of the search depth we know it's sufficient so we don't really need to worry about it and then simply URL as string there we are this is the URL we can simply download or asset from and lastly let's get the image we can display for this product we've done this before as well the const destructured image is going to be equal to the product. images at the index of zero so the first image that we want to display for this product and then because we're in an unordered list let's return An Li a list element right in here and because we're mapping of course with a key and this can be the product. ID for example the class name is going to be Flex a space X of 6 and a padding y of 6 as well let's open this up in here goes a div with a class name of relative a height 24 and a width 24 and inside of here one more conditional check where the type of image and if it's unlike a string is not like the string and we have the image. URL so same track as before in that case we're going to render out a next ja image and otherwise just nothing so if the image is an actual image let's render it out as such with a fill property with the source being the image. URL with the alt tag being a dynamic string so for example the product. name image H so lemon milk image or whatever your product name is and as the class name for this image let's pass flex none a rounded medium a BG gray of 100 an object cover and an object Center as well Center there we go let's save that see what happens there is our product beautiful after the closing div let's initialize a new div right here with a class name of flex Auto a flex a flex-all for vertical alignment and I switch keyboard again so Flex Das Auto there we go flex call right here and AD justify Dash between inside of this one more div with a class name of space y of one and inside of here let's put an H3 tag containing the product do name just like so and let's give it a class name that's going to be text Gray of 900 perfect save that see what happens there it is beautiful and also let's put a P tag right below the H3 containing the category and this is nothing else than the label we have calculated right uh here inside of the product mapping this this is where we put it and let's give this P this ptag a margin y of one for a bit of vertical spacing as well awesome now comes a very interesting part and that is the download button so if the order doore is paid we know the uh user has paid successfully and we can offer them the option to download this image if they haven't paid we're going to render out n they won't get the download link and this is nothing else than a regular a tag where not using a nextjs link because this is not for navigation this is just for download and the ITF is going to go to the download URL we have calculated above the download is going to be the product. name so this is going to be the name of the file that the user downloads essentially the same as the product name right here the lemon milk for example and let's give it a class name of text blue of 600 on Hover an underline and an underline offset of two right here perfect and inside of here we can for example say download asset let's see what that looks like here it is let's click it and this will download our actual product file that the user has paid for beautiful very very nice let's finish up the styling right here so for example below the below the closing div below the closing div and before the closing Li element let's put a P tag and this is going to be with a class of flex n a font medium and a text Gray of 900 inside of here let's put the format price utility we have with the product. um price and by the way if you're wondering why I'm kind of going faster it's because I already cut most of the video and we only have like about 2 hours left and I still want to do a couple things with you for example the footer the newsletter would be nice to make and then the mobile Navar as well um so those are kind of couple things that are still important um to do and YouTube I think uh only lets me upload a 12-hour video so that's why I'm going a bit faster because once we're done with this I want you to have a really really nice app that you can show off to your fans on your portfolio and whatever okay so let's continue below the unordered list let's create a development with a class name of space Y6 a border of t a border gray 200 a padding top of six a text of small a font Das medium and a text muted foreground for or text styling awesome inside of this div let's give it a bit more space there we go let's create one more div with a class name of flex and justify between and inside of here we're going to say in a P tag subtotal and then right below that in another P tag we can simply use our format price helper format price and pass something in here now what do we want to pass in here of course the um you know the total cost for all the products in this order oops I didn't mean to click the download button but we still need to calculate that remember we have access to all the products in the order but we don't know the total price yet so the con order total very straightforward to calculate this can be a products. reduce and the products once again we know what those are those are going to be cons products are going to come from the order. products typescript isn't sure what this is could be a string for all typescript knows we know it's not so it's totally fine to cast it as a product array type right here so that we can then reduce the entire array to one single value we get the total and the current product so essentially it's like a map but only one returning value from here where we for each iteration return the total plus the product dot oh did I switch to English keyboard no no product. price there we go and this is not times but plus right here and of course we need to tell it what the initial value is and that can be zero and did I okay I there we go I I thought I messed up the keyboard again but apparently everything works that will be the total order amount for this purchase and we can display it right down here in our format price helper let's save that see what it looks like and we can see a subtotal show up right here very very nice let's copy this entire thing down once and instead of the subtotal this is now going to be the transaction fee and we can simply call the format price with a number of one for example and let's also apply a class name to this ptag that is going to be text Gray 900 and we also I forgot that want to do that for the price of the subtotal so also here text Gray 900 save that looks a lot better very very very nice and let's create one more div right below here with a class name of flex an items St Center a justify between a border of t for top a border gray 200 a padding top of six there we go and a text Gray of 900 awesome let's open up this div and inside of here let's create one more div with a class name of text-base and in here actually let's change this to a tag I think that will make more sense in HTML semantics let's say total and it will match the pattern from right above and right below here let's create one more P tag and this is going to get a class name of text base as well um to kind of break out of the small we apply to the rest to all of them and inside of here once again our format price utility with the order total plus one so plus the transaction fee that we are applying again if you wanted a different transaction fee insert it right here and you could even put this into a central config if you wanted to and for this project this would be you know complete Overkill to be honest and let's save that see what happens and we should see our total pop up in a beautiful kind of way right here on the right hand side okay with one closing div right here after the pag and one more and with four closing divs to go this is where we're going to create one last div for this entire component and this is going to be with a class name of margin toop 16 a border t a border gray 200 oops border gray 200 a padding y of six and a text- write this time inside of here let's create an nextjs link element and this is going to contain an H of Slash so it will take us to the homepage this could also actually let's change this let's do this to slash products where all products will be listed the class name of this link is going to be text small a font medium a text blue 600 and lastly on Hover a text blue 500 so a bit lighter and in here we're going to say continue shopping and then we can even put an end a or that is r a r r right arrow semicolon there we go continue shopping and that should show up right here beautifully in the bottom right where when we click it we get taken to the page where all the products are listed that doesn't quite exist yet but we've already done all the Logic for this page this is going to be super simple super nice to create you're going to see exactly what I mean that by that in a second beforehand however remember the entire step where we need to PLL if the payment was already made um that stripe will notify us of right now we're saying yes this payment was made in our database but that's of course not what we want in the long run we want to know really from stripe was this paid or not and for that we need one custom component in the thank you pay page which will be a client side component and that is going to be the payment status we are going to create this is a custom component that doesn't exist yet so let's go in our components and create a new file called payment status. TSX now this payment status component logic is going to be very simple actually but first let's create it the con payment status nothing else than once again an arrow function that we can export default at the very bottom the payment status right here perfect and this is going to return nothing else than a div at the top level that's going to get a class name of margin top 16 a grid a grid calls of two a gap X of four a text small and a text Gray of 600 there we go let's open it up and create one more div in here this is just for layouting it won't get any class name and a P tag right in here with a class name of font medium and a text Gray of 900 and this will say shipping to by the way the reason we are creating a custom client site oh by the way client site use client at the very top the reason we are creating a custom client site component is that so the thank you page can remain as a server component that has the benefit of we can perform server s side validation and actions so the page will never actually be rendered unless the user is logged in and it's there order that's what we make sure here um otherwise there won't even be a Content Flash and then for the polling which needs to be done client side that's what we can do right here in one component a client side component on the page that we can import and embed in the server side page so we get the best of both worlds client s side interactivity and server s side you know no content flashing logic done securely on the server and that's just a really smart architecture to go about this okay right below the shipping to Let's create a new P tag and this will contain the order email the email of the uh person that um made this order basically that's all it is and we can simply receive that as a prop in this component because we already have access to that in the parent so we can destructure the order email as a prop we can already expect the order ID as a prop and last thing we need in this component as a prop is the is paid property these all come from the payment status s of course an interface that we Define ourself to let typescript know what type these destructured properties will be so let's define it the interface payment status props will contain these three things and both of the order email will be a string as well as the order ID will be a string and then lastly the is paid property will be a Boolean again I know I'm at a faster Pace now but this is only because I want to leave you with a complete nice project and I'd rather you know talk a bit faster and do this a bit faster with you and you have the complete nice project in the end than you know us having to uh you know not do one feature I would like to avoid that and have you have something complete that you can show off awesome so we've got all these three let's pass them in from where we call this client side component for example the is paade very simple that's simply the order doore isaid property the order email you can probably guess is going to be the and then the order. user let's cast it again as the user CU this technically could be a string for all typescript knows but we can cast it like this and tell typescript yes this is definitely a user because of the search depth again this is totally fine. email and last thing we want to pass is the order ID very straightforward you can probably guess it's the order. ID nothing else and that's all we need to pass and receive in or payment status to get up and running and actually validate with stripe that this payment was indeed successful below the order email in this component let's open this up in a side by side so we can see what we are even doing here shipping to hello atj coding.com very nice so right below here with one closing div to go let's create the last div of this component again just for layouting this won't get any class name and inside of here let's create a P tag with a font medium and a text Gray of 9900 once again this is going to say order status and let's do a check if the or one ptag below here and in here a check if the is paid property is true in that case we're going to say payment successful and in the other case we're going to say pending payment because we haven't received the payment just yet but that's exactly what this component is for so we can poll on that data now where are we going to pull on the data though and the answer is that's going to happen in the payment router in a logic that is really simple essentially all we're going to do is ask our database is this field right here the is pay property in the very bottom right here is it true or not that's all the logic for it and we can simply do that in our payment router because this is you know related to everything with a payment and we can say create a new API um route in here and call it the poll order status this is going to be a public procedure we could also make this a private procedure if we wanted to it doesn't really matter in this instance because there is no security risk in you know telling people if an order is paid or not um you could always mark this as a private procedure as well I guess we can just go with that and the input in here will be a z. object and for the input that we want to receive it's simply the order ID that we need um to pull for and this is going to be a z do string and as for the actual logic this will happen in a DOT query because after all we're only getting data so a query is completely sufficient for this let's give this a bit less space right here there we go and now for the logic first off let's get the order ID from the input so which order are we checking for um if it's done or not and of course you need to destructure the input in our query to even be able to access that and while we're here we can mark this query as asynchronous so we can perform database operations in here using or cons payload is equal to await get payload client so we can ask our database hey is this order paid or not so let's get the order the cons docs orders you know the drill by now is going to be equal to await payload do find so we're finding one certain order in or database so of course the collection will be from the orders and which order do we want to find that goes in the where block right here where the ID of the order equals the order ID that we pass in to this API route right here now if there is no orders. length so that just means the array doesn't contain an element we don't we didn't find the order we can simply throw a new trpc error and this could be something like code not oops not found as a string so we didn't find the order have requested and otherwise we did find it and can get the order from the array because remember this is an array and we only care about the one order that is in that array so we can get the order by array destructuring from the orders and simply return an object where the is paid value is the order doore is paid which comes straight from the database you know so we're just saying the is paid value right here matches the is paid value from the database and will be evaluated as a Boolean so that when we go back to the payment status right here we can now pull this endpoint by saying or worrying about the destruction later as always we can simply say trpc do payment. pole order status automatically added to our API endpoint super nice dot oops do usequery right here as the first you know parameter or argument rather we need to pass in here it's the order ID which we receive as a property into this component so you know no no extra logic needed and as for the configuration object we can pass as the second one here this will be two things first of the enabled value and this is going to be the is paid is triple equal to false right here so this is only enabled if this Boolean right here evaluates a true so we're only querying as long as the order is not paid yet and then the refetch interval we can also pass pass as the second option here this gives us actually a function where we can receive the data as the arrow function kind of um input right here and can do a check if the data which is optional by the way it could be undefined uh let me show you that could be undefined right here so we need to mark it as optional dot is paid in that case let's return false and otherwise let's return 1,000 so we are going to stop querying if the data is paid or if the um order is paid and otherwise we're going to make a request to our API endpoint every 1 second to basically ask hey is this product paid or is it not and once it is paid let's do a use effect we now know that the data came back successfully and the user has actually paid their product stripe notified notified us of it therefore let's say if the data do is is paid by the way the data we can simply destructure from this query which will be the result of the API point that we are querying if the data comes back as is paid we know everything is good we got the money we're going to say router. refresh right here and once again the router just a utility um hook cons routers equal to use router we can get from next SL navigation important not next SL router but next SL navigation because we're working in the modern nextjs and of course we want to pass this use effect a dependency array to rerun whenever the data do is pay changes and also just for simpl or just for um completeness sake when the router changes because that is also an external constant that we are using in this use effect and this needs to be data and not date of course so the refresh will refresh things like the download button the nav bar and whatnot essentially the entire page and let the user know hey everything is good we got your money the payment is successful so we can display all the different states and that's the logic that's the entire thinking behind this whole check out flow where the payment is made the web Hook is triggered um and the user is sent to the thank you page and then we are essentially polling our database um if the order was paid and if it is then we are displaying the download links um for example awesome so about this web hook right where do we receive that and the answer is currently stripe has no way of notifying us whenever the payment was successful because we're never listening to that event anywhere so the question The Logical question would now be where should we listen to that event and the answer is in or server this is where we can easily receive the request from stripe that we can listen to um we can Define that in the stripe dashboard whenever an order was paid successfully and then react accordingly so let's get started with that it's it's really important we need to know when the when we receive the money essentially you know um so there's one thing we need to configure in order to receive this message from stripe and that is going to be our web hook middleware let's say con web hook middleware and this is going to be a body parser now what is a body parser essentially it's a separate package we can install that goes very well with Express and that will allow us to receive the proper notific from stripe let's say yarn at body oops body parser great that's installed and the reason we're doing this is because we kind of need to modify the message that stripe sends us okay in order to be able to use this we need to import it in our server dos and let's do Json inside of here we get a verify function we can simply call and this gets us the request this will be of type web hook request now where does this web hook request come from this is going to be a custom type we're going to Define right here so export type web hook request that is because we want to slightly modify this request in order for us to make it readable and check if the message actually comes from stripe very very important because it will have a certain signature that we need to um validate on or end to make sure it's actually stripe and not just anyone I'm trying to call or webook this will be of type incoming oops incoming message that we get from a HTTP and we will extend this by saying and the raw body which will be of type buffer so we're going to have a custom property that is going to be the raw body right on here and there's no Advanced logic at all involved in doing this it's simply the third parameter we get passed into this Arrow function or argument whatever it's the buffer and we can simply assign the request do raw buffer oops raw raw body to be equal to this buffer that we can then use in our web hook to validate if this actually comes from strap and of course this needs to be an error function so we don't get any syntax error and now the only thing left is whenever somebody makes a post request to our app app. poost to SL API oh this needs to go in a string SL API SL web hooks SL stripe which is what we're going to configure in the stripe dashboard then we're going to use our web hook middleware first so we have the rec. raw body available wherever we handle this and this will be our stripe web hook Handler nothing else than a custom function that we can Define now this strip web hook Handler will live in a new file and let's create it in our source directory and let's call it web hooks. TS if you had any other web hooks they could also easily live in here from here let's export our con stripe web hook Handler exactly what we call called it in the other file this is going to be an asynchronous arrow function just like that and we are going to receive two things in here the request which will be oops request req which will be an Express that we still need to import import Express from Express there we go so we can simply call the express. request and also the response will be which will be the express. response there we go now as I said earlier this video is getting pretty long and I want you to be left with a complete very nice working application so I do not think it makes sense to write this entire web hook Handler ourself because there is a lot of boiler plate involved in this but first before we um copy it from the copy and past list where I prepared it completely for you let's think about what it does because I want you to understand why we're doing what we're doing first off what we're going to do in the web hook Handler conceptually is validate that this request actually comes from stripe don't don't mind the typo here very very important only stripe should be allowed to send us a request to this web hook because essentially it unlocks the content for the user that has paid if any user could call it anyone could get access to any file on our platform and that would be really really bad and if it is actually stripe calling this web hook in that case the only thing we basically are doing is update theore is paid value of this order that we pass as the metadata by the way because if you remember when we when we create this session the stripe session right here we are passing metadata the user ID and the order ID and that will actually be sent to the web hook that's why it's metadata it will be available to us in this web hook as well so we can use it to find the order and the user and simply update the is paid value as long as we're sure that this is actually stripe calling this web hook and once that is done last thing is send Reed email we're going to send a super professional nice looking received email to the user that has made a purchase on our website and as long as you understand conceptually what we're doing here and it makes sense to you I think this is no problem at all to go into a copy paste list and simply grab everything in order to leave you with a complete nice and project as I mentioned earlier I'm not just doing this um I want to save some time and we can simply go ahead and paste all that boiler plate in the inner function right here now there won't be um All Imports resolved right away for example the web hook request we can still import that from our server so same thing with the Stripe Right Here we do need a um a few Imports here um so for example stripe we are only using that for the type so at the very top of the file we can say import Stripe from stripe there we are and we can simply import only the type that's totally enough for our use case here we need access to our database client simply import that and we also need the product type simply import that and then there's two things unresolved right here where we send the Reed email so kind of the last step so let's quickly go through this you saw conceptually what we're doing essentially this is step one making sure this is actually from stripe this is step two update the is paid um property to be true right here and then the last step is to send the received email um using resent so it actually lands in the user inbox and not in something like spam for example so where does this resent come from and very easy we can go to the very top of the file and say const resent is going to be equal to a new resent something we can import import there we go resent from resent a library that is the same from the email provider that we're using throughout this video anyways to send all the emails remember they have their own package so that we can simply install it yarn add resent to send the emails and the only thing they expect in this class is the process. env. resent API key that we have already defined so we can check that right here it's the same key as for the rest of the email sending for example on the um account creation so we can simply put it in here and instantiate a new recent class now the last thing we want to do in here is the received um email HTML and this Reed email HTML is nothing else than a custom react component so we can create a new folder in our components and let's call this emails and the first email we're going to create as a file is the received email. TSX and now if you're wondering what should go in here let's start with the normal react component this is let's export cons received email and this is again the arrow function where we can receive props and return the email HTML um based on what we want essentially the data that we need in this component are going to be four things the email the date the order ID and lastly the products that this person ordered and we want to put in the receipt of course we still need to define the type of those so the interface let's call it um received email props just like that and where we Define all those four Fields the email is going to be a string the date is going to be a date the order ID is going to be a string and lastly the products are going to be of type product um array just like that and remember we still need to assign that interface the received email props to tell typescript these will be of that type and also remember we don't want absolute Imports but let's change this to a relative import so we don't get any er um don't get any issues in the deployment stage here in a bit awesome now before R turning anything from this component we still want to calculate one thing and that is the total price of all the products and you should know how to do this by now the cons total is nothing else than a reduced array of all these products up here so the products oops products. ruce there we go we get the accumulator and the current value and for each iteration we simply want the accumulator plus the current price and we also want to start at zero right here and at the very end we can add one and this is going to going to be our transaction fee if you remember now for the return I could tell you that I spent weeks and weeks designing the perfect email but all it is is uh react email templates it's just a template that I adjusted for or use case for example change the um image and whatever but essentially there's a demo. react. website where there's a bunch of nice website presets like this receipt that we are going to use which originally is an apple receipt and I just cut out some things removed some things but the only um thing I want to highlight here is why I'm showing you this this is just a front and just a starting thing there is no logic in here that we can learn from and this is mainly just regular CSS so I really think there's no point of writing this ourself and instead let's just grab the body and the CSS styles from the copy paste list we will know it will look great and again I don't think there's genuine value to learn here it's just a styling thing if you like you can take the time to style this that's completely fine but that's not something we're going to focus on in this video I don't think it makes sense um so let's grab the received email body I'm going to provide that to you all in here um starting and ending with the HTML and that's what we're going to return from this component and if you're wondering why there are so many squiggly lines right now that is because these are actually all custom components coming from a light library and that is called at react d/ components so this is a library oops ad of course this is a library that is um you know made for writing Nic looking emails I think you can even use Tailwind for this but once again I just kind of copied their template changed some things so they work for or app and now we can import all these components after reloading the window probably to make the automatic Imports work and uh you know just import them into our received email so this email will be sent once a user has successfully um did their purchase and we should receive the wet squiggly lines once the language features are done initializing there we are okay we still don't get them then let's import them manually whatever let's import the body container column the head HR HTML IMG for image the link preview so it say entally all the tags we're using in theed email the row the section and lastly the text from at react email/ components there we go that should resolve most of the errors we have in here and now all that's left is the CSS styling you can see those styles are currently not applied here is our format price we can already import that and change it from a absolute import which it will be by default to a relative import by saying dot dot dot dot there we go perfect and then the only thing left in here seems like are all the Styles so I also provided them in the copy paste list you can simply grab them this is not a CSS tutorial and paste them at the very bottom of our file now the only thing that's left is one function and that is the where is it right here the format and this comes from a one more package that is called yarn ad date FNS data fense is a utility library that makes it super easy and enjoyable to work with dates in JavaScript which by default it's pretty annoying but with dat of ends we get a function that is called format let's import it at the very top import format from date FN it lets us convert a date to where did we use it format price uh uh uh no okay there we go that lets us format a date into a custom string format very easily and it just looks good out of the box and that's our receipt email I hope you understand why we copy pasted this because I also want to get this deployed with you and again this video is not about CSS so okay now to get the received email HTML this is going to be you know this is the standard react component that we are exporting here but when we send an email to the user it can only contain the actual HTML so the email as straight up text and in order to convert that we can simply Define an export cons received email HTML which does nothing else than take this email above and convert it to HTML so the props are going to be the exact same weed in the main component the received oops received email props that we can pass in here and this is just going to be an arrow function that Returns the render function from react email/ components and in here we can pass in a JavaScript X component in our case the received email a JavaScript X just a jsx component you know regular plain ass react component that we can pass in here and we can also spread in all the properties that we receive for this function and one thing we can also do is pass it a configuration object where we say the pretty is going to be true save that now we can import the receed email HTML in our main web hook file and lastly import the web hook Handler in inside of our server. TS file everything coming together all of a sudden very very nice and now the biggest last step is to get this deployed so we can already test out the strip web hook Handler if everything works oops didn't mean to do that now there are one or two more steps that we can do for example the products page where all the products are listed we didn't forget that we're going to do that together um for now we want to get this project deployed and in order to see if everything works correctly let's already yarn um let's already run a linter this will find any potential code issues in our code that would prevent a build from happening and it seems like we currently don't get any linting errors that's very very good and then let's head into our package.json and Define a build script so what will be run when we um you know want to deploy this project we can see there is already a build script but that's not exactly what we want we're going to slightly modify these build scripts in order to account for the custom infrastructure the custom server that we have in this build or you know what it doesn't make sense to write all of these ourself either nor does it make sense for the start command let's copy them over from the copy paste list paste them into oops or package.json and that's going to allow us um to do the deployment so essentially if you want you can definitely get further into this um again this is not the main focus of the video but um essentially we're building payload as one step build building the server as the second step and building nextjs as the third step so we're building our entire project in three different steps and then combining all of them in one simple build command and we can start the built out version using the start command also in production so once we save that let's try running this let's run yarn build and that will build or entire project and it seems like currently it's okay option bundler can only be used when module is set to es 2015 or later and this happens in the TS config so let's go over there let's open or tsconfig dojon and change the module resolution here from currently bundler and we should change it to let's see node there we go the module right here let's change it from es next over to commonjs and that can stay as it is with all the rest let's try try this again let's run our yarn build command and see if it fails again okay and we do get one more error and that is originating from our receipt email that react refers to a UMD Global but the current file is a module so what we can do to get around that or to fix it is to Simply say import Star as react from react because it doesn't really know what react is so let's try running or build once again all right one more error by the way this is nothing unusual by the way if you build for the first time no matter what project or how perfect you laid it out beforehand there's always going to be some errors but you can definitely get through them this is not a big um problem at all so let's um complete or let's fix this one copy files is not recognized as an internal or external command operable or program or batch file that's why we can say yarn at minus D and that's a dependency copy files we can simply we're using it right here by the way to copy over the static files that don't change dynamically inside or this folder with that depending see installed let's try building again and we should get one step further this time and maybe we get a different error maybe it works now let's give it some time and see okay it's still in the same process it connected to mongodb server successfully that's great that's a point we didn't get to before so that seems like it did the payload thing it did the server thing and now it's building next chest but I wonder do we even have a build command oh okay there's something missing in our server so it actually started the development server now and it didn't actually build our next Jaz project because that is something we can Define ourself in the um in the server. so what should happen when nexts is building so let's add or build script in the server to build this for production if the process. env. nextore score build is true in that case we're going to say app. listen and this takes a callback function just like so as the second uh argument by the way the first one is going to be our Port oops Port there we go and then in the Callback function right here we can say payload do logger doino and saying something like you know nextjs is building for oops building for production and then we can call the wait next build just like that oops next build which is nothing else than an import we can get from next chest to build this project for production so import next build and this comes from next SL this SLB build there we go it's a utility function we can use to build this project ourself and by the way let's pass something into here and that's going to be the path dot oops we still need to import the path from path do join and we're going to join the current dur name the directory name we are currently in with um as a string and then dot do slash now this is going to give us an error that's totally cool by the way one thing we that's not totally cool is the await error but the next build error is something that is expected this works we can simply ignore the error or expected the S expect error and the error doesn't matter it builds very very nicely and we're going to see that here in a minute and then we're going to say process. exit if we are done with the build and then let's return from here because there's no point starting up the dev server um if we already built our app for production so let's try that again I mean syntactically and everything this should work now we saw the build go through successfully but before it just started or development server and actually not built for production so let's see if it works at this time and what happened now cannot read properties of undefined reading logger that's probably just an issue where the yeah where the payload client is not defined yet so let's put that right at the very top actually right below the let's put it below here below the app. post before we need it um so payload will actually be um initialized because if there is no payout of course we can't use the logger for it and let's try that again oh property class name does not exist on type dialogue portal props fail to compile that's funny cuz that's the exact same issue I had um before and because this is a UI component and we know this works we can simply ignore this error right here and we can also ignore it um right here so we know this works the sheet works just as expected it's just a typescript thing so let's try that again once more and uh that arrrow will be gone hopefully it will go through now generating static pages that seems very nice collecting build traces no error yet and it might just go through this time while it does let's already navigate over to railway. apppp the place where we are going to deploy this application let's go into side by side by the way give it a bit more space and by the way the build was successful very very nice we know it works that's why we can easily deploy it now and I'm going to log in and then let's meet in the dashboard let's create a new project once we're in the dashboard right here and let's actually get this project up to GitHub so let's go to github.com let's create a new repository let's go here and then new and let's call this digital uh hippo and I believe I already have one repo name that so let's name it uh oh all that works I don't have uh one yet let's make it as private and let's create this repository right here in GitHub and let's copy down the origin command that's pretty much the only thing we need to worry about um is the remote origin so that once we are in our project we can simply say get add dot that means add all the files in our entire project to our um to our git we're going to commit this by saying git commit minus M and then for example initial commit doesn't really matter and uh then let's paste in the git remote origin so we can add that and then simply Push by saying git push minus U um for upstream and then origin and then the branch we want to push to which in our case is main as we can see right here hit enter and okay never mind might be Master actually let's push to master and that should work then if it's not main just push to master that works all right very nice let's refresh the page and we should see okay that's just a little note I made for myself when I um you know ended One recording so I know where we left off by the way that's you know you shouldn't do what I just did I pushed the EnV file I don't know know why it's not ignored in the GitHub uh in the G ignore by default that's really really stupid get ignore that should definitely be a do EnV right here um I don't care right now I mean you saw my EnV anyways but if you're pushing this into your GitHub repository make sure you add the EnV and do not commit your sensitive information to your GitHub repository very very important don't do what I just did all the rest is totally fine just we won't we don't want to see the v file end up um in here again right now I don't really care you saw my values anyways but uh just for you it wouldn't be a good idea um to do that and just like that we can now say digital hippo that's not the prod but in Railway we should be able to see digital hippo right here I have the production version and this is my local version or or local version right here on the left hand side and we can simply click deploy now let's deploy this project and also add add some environment um variables all the ones that we have in ORV for example we can already abort the first um deployment right here we don't really care about it because we haven't set the environment variables yet let's declare them so in the we found some variables in your repo that you might need oh so it already got them from our. EnV file I mean okay but we can just paste them in the Raw editor here if you don't find the option is right here raw editor click that paste or entire um EnV file and we later on want to change the next public server URL but for now we haven't gotten that URL yet we can generate a domain right here um and this is going to say digital hippo production up. railway. app that's fine we can copy this um URL right here and then let's head over to the variables so we can change the next public server URL to where we are actually deploying this project and this needs to be the https sl/ digital hippo blah blah blah what we just received from Railway by the way while this is loading we can already go ahead into our stripe dashboard and create a new web hook that um gets triggered when somebody purchases a product from our app I already have a few web hooks but in the top right here we're going to say click uh um add endpoint I'm going to log in right here as for the endpoint URL we're going to say what we just copied from Railway where our app is going to be deployed https colon colon or SL slash rather in front of it and then the slash API web hooks SL stripe so basically what we have in our server dots whenever a request made is here we're going to handle it in the stripe web hook Handler and that's nothing else than the endpoint URL we're going to say right here perfect and the event we want to listen to is is going to be the checkout. session. completed that's the payment success event we can simply add and then add this endpoint right here that's going to create web hook and whenever we now order something it should call or server that by the way seems like it errored somewhere uh uh uh do a dynamic import which is available in all commonjs modules okay I'm going to take a second to debug and then let's fix this issue all right and I figured it out to be honest it was a very simple change but not the most intuitive because there was a little package Inc compatibility so there are three things that we still need to do in this application first one is fixing this issue so fix it oops fixes there we go so that's going to involve you know getting our build to run correctly in the darker container for or you know deployment so this runs correctly and we can try out the web hook with stripe then there's security we want to finish some security settings and to make this app production ready and the last thing we're going to do is some styling for example you know adding a few more product reals to our main page um doing the mobile Navar for example or improving the styling of the um the email that we get when we for example sign up to our application which right now is just you know please verify or also the metadata right now it says create next app with the verell iccon that's not what want and that's also going to fall under styling so these three things right here and the first one we're going to start with is by far the easiest so the fixes how did I fix the issue of our project not building in the deployment environment it's very simple there's one package incompatibility and we also want to add a main this/ server.js right up here I don't think this is necessary for the project to work in the deployment environment but it is a good practice nonetheless so we know where the main entry point to this file is or to this project is and then right here under resolutions we are going to use version 7.0.2 for the CLI UI package it's a dependency from our copy files um package right here and the version 8 apparently doesn't really work um with our other dependencies so we're going to say hey use the 7.0.2 version now if you already want to try this out there's one more thing we want to do and that is configure the stripe web hook secret what does that mean if we go to our web hooks for stripe and currently we do not even have the possibility for the web hook to run correctly because if you take a look at our web hook Handler right here we expect something called a stripe web hook secret this is how we make sure that the um the center of this is actually stripe you know via a secret and where do we get the secret well we can get it from the web hook we configured in stripe by by the way right here you can see that I've already tried this out once and the web Hook was triggered successfully the entire checkout flow just worked and right here in the top right corner under signing secret is where you can hit reveal and after entering or password it should give us the signing secret right here that we can copy over go into our EnV file. EnV right here and simply put it right here for example the stripe uncore webhook secret and and paste this value we just got from stripe but much more importantly is that you also configure this environment variable in your production setting the stripe web hook secret right here just the exact same thing also in the production setting so we know that these requests to our web hook are actually from stripe and then that's automatically going to redeploy your service now if you want go ahead and try this out I encourage you to do it I already tried this and it works super super well it builds correctly and now the copy files command actually runs so that's amazing awesome okay now to the let's see where we are that's the fix is done by the way we can move on let's make this kind of like a red color if we can there we go fix is done let's move on to the security we want this to be a production ready app therefore we need to add some security features um to it most of which we've already done but let's Implement some more to finish this so for example let's start in or server. TS and protect the card page so you have to be logged in in order to be able to go to the card page so how are we going to do that first off let's say cons card router so a special router for this URL is going to be equal to express. router there we go and then there's one super nice middleware that we can use and that is the card router. use so just like with Express and in here we can say payload do authenticate and that's going to attach the user object to our Express requests just like that so we can now say um not cons but card router doget and whenever we get the main page the slash of where we are using the card router then we also get an arrow function containing the request and the response this is very standard um you know Express stuff just like that there we go and we can now get the user from the request so the const request is going to be the wre as and then a payload request we can just cast that type and if we do not have a request. user in that case they shouldn't be able to access the slash card page so we're going to say return res. redirect and we're going to redirect them to the/ sign-in and with the origin oops origin of card so they get sent back to the card once they have signed in successfully next up we want to get the current URL and parse that let's say the const pars URL is going to be equal to parse and parse comes from let's see at the very top right here pars comes from URL that's already you know not really installed but that's a built-in thing you don't have to install a package for that you can just destructure the import it's a named input from the pars you know package and what do we want to pass well the rec. URL and we're going to pass true as the second argument right here now why are we doing this you're going to see that in this step right here and that is because we need to tell next GS what to render when the user is authenticated and in our case well they are logged in everything is good so we're going to say return next app which is nothing else than nextjs do render and we now want to render out the actual card page so we need to pass the request the response the um you know the URL or the path name which is SLC card and lastly the par urlquery this won't work without the query don't ask ask me why this is according to the nextjs documentation I'm sure there's some you know reason that makes sense behind it I wouldn't know to be honest and but if we don't pass the parse query then next J is not going to be very happy with us and last thing we want to do is to say app.use to actually use this middleware with Express we have just created and the router so for the slash card route we are going to use the card router and just like that we have protected the SLC card page only to be available to users that are authenticated similarly in the terms of security we're at right now is to add a nextjs middleware so let's go under Source create a new file called middleware dots in here let's say export async function Middle where it needs to be named this way by the way and from nextjs we receive the request of type next request there we go we can simply import that and in this middleware we're we're going to do some very basic logic checking that is if the user is logged in they shouldn't be able to access the sign in and sign up page that's all it is so first off let's say const and then destructure from the request now what do we want to destructure the next URL and also the cookies so that in the next step we can get the currently logged in user the const user destructured is going to be equal to await get server side user because again this is on the server side it's a server side middleware and we can simply pass in the cookies we get access to in the middleware in here to get the currently logged in user and now for the important check if we have a user and the in in Array slash sign in or as the next array element SL signup do includes the next URL do path name that means the user is trying to access one of these routes and is logged in because we have a user right here that's how we know they are logged in in that case they shouldn't be able to do this because they are already logged in it doesn't make sense so we're going to return a next response. redirect and we need to import the next response from next server up here and we are going to redirect to or process.env dopu server RL and then simply you know slash because by default the process. EnV is with out a trading slash so we're going to add it right here behind that environment variable awesome and the other case is the user is not logged in or not trying to access one of these routes so everything is fine and we can simply return a next response. next so we're saying everything is okay just perform the next action and let the user do whatever they want to do awesome let's finish up the security and security also needs to happen in our users collection because right now for example anyone can read all users anyone can create a user and so on and there's just some security considerations we need to do in the users collection right here and all is going to start in the fields for example one user needs to have a list of products so for data Integrity you know if we take a look at how we architected this entire thing let's zoom out a lot then we can see that where was it each user there we go has multiple products right now we're never modeling that anywhere in our data so let's do that let's add an object right here a new field and the name is going to be products the label is going to be uppercase or capitalized products the admin condition that we have done before so that's determining if we can see this in the admin dashboard or not is going to be a function that always returns false there's no point in showing it the type is going to be of relationship and the relation to of course is going to be to the products collection now we can also add a has many of true because one user can have you know 10 products that's totally cool and also let's add the product files by the way we can simply copy and paste down using shift alt and arrow down this products field right here and slightly change it for example let's change the name to product uncore oops underscore productor files there we go the label can be product files the condition is going to stay the exact same so is the type it's also going to be a relationship and the relation to is going to be to productor files there we go okay and now for the access rules who should be able to read a user and the answer is well the admins and the user itself so let's call this admins and user right here in our access policy for the users collection and to find this function right above let's say con admins and user and we can already tell typescript this will return return in access policy so typescript will be happy and this is going to be a regular Arrow function in which we can destructure the um request and from that the user right away so we can work with it so for example if the user. roll is admin triple equal to admin well in that case yes they should be able to access the user so we're going to return true unconditionally and otherwise we're going to return the ID of the user and if that is equal to the user. ID so you can access the user that matches the currently logged in user ID which is yourself so only admins and the currently logged in user can view the currently logged in user if that makes sense the create can be true because anyone is allowed to sign up to our services and therefore create a user that is totally fine the update let's change that so only admins can update a user or we can through code that's always an option as as well so we can destructure the request right here and simply return If the rec. user. roll is triple equal to admin you know just like we've done before and same thing we can copy and paste this down using shift alt and arrow down and change this to the delete so only admins or we through an API route for example can delete users and last thing we want to change in here is for this to have some nice admin visibility settings so let's separate this by a comma from the fields first and then say this should be hidden in the admin panel if and this is a function as well an arrow function where we can destructure the user that is making the request and simply return the user. roll is unlike admin so this will be hidden for everyone who is not an admin and we can also pass this the default Columns of ID and that's just going to change how this is displayed in the admin panel but just a tiny detail that's not too important very nice and while we are here we can already turn this horrible generate email HTML into a proper professional email that is sent to the user and what I promised what we're going to do earlier so instead of just the a tag containing the text of you know verify account or please verify or what we had whatever let's create a new professional looking email and we're going to do that in our source folder under components because it's just a single component nothing else than the received email for example let's create a new file called primary action email. TSX right here now this component again is just a regular react component so let's say xort const email template and this will be a regular Arrow function just like all the other components where we can destructure a couple of props so let's call these props for example the email template props oops props and Define the interface for this email template right above so typescript knows what's going on the interface email template props are going to be three things that's going to be the action label of typ string you're going to see what all these do here in a second then the button text what's going to be shown on the big button in the email that's going to be a string as well and lastly the H ref where the button will take the user that's going to be a string as well now in order to use these props in our actual email template let's destructure them the actual label and destructure them right away from here and then simply return the actual email again this is not a video about HTML and CSS which an email just is so I prepared the HTML for you it's right here under the primary email section in the copy paste list and we still need to import all these components from react email but again it's not a lot let's import the preview from at react d/ components so with the body same thing with the container same thing with the IMG this is not nextjs image this is just a regular you know HTM image that looks good the section and we also need to import the button from react email/ components and the HR the horizontal line as well beautiful and we can simply copy and paste the CSS styling it's not a lot it's very little HTML styling and we can simply paste it right here at the very bottom perfect now in order to use the HTML as actual HTML and by the way also we need to return the HTML we can't just past it as is in there we need to return this from the component because again this is nothing else than a standard react component and now to access the actual HTML as a string from this email this is as straightforward as with the received email if you remember how we did it there we can export a const and call it you know primary action email HTML for example and this is again an aror function that receives the same props as the component so the props are going to be of type email template props and inside of here we can call the render function from react email/ components and we're going to render out the component above the email template this is going to be self-closing and we're simply going to spread in all the props like so because we know they are the exact same and we can also pass this a configuration object where the pretty property is going to be true perfect what that means is now we can use that in our users collection and tell or CMS payload how we want this email to look so instead of this horribly looking a tag we can simply Now call the function that we have just exported from the primary action email the primary action email HTML and pass the email all the props that we want to have for example the action label is going to be verif oops verify your account then the button text is going to be verify account and lastly the hre we're going to pass pass in here is going to be a template string with the first element being a dynamic interpolation of the process Dov dopu server URL server URL so wherever we Host this service then slash verify Das email and then attach the token remember so the question mark token to pass it as a query parameter is going to be equal to the Token that we get access to right here um in the arrow function oh by the way the reason we are getting an error here is because we're not actually returning the email HTML but we're simply executing a code block right here so let's change this from being a code block to directly returning the render and that's the error gone in the users's collection we are now returning the stringified HTML that we can actually use for our email beautiful now if you remember okay the email looks good now if you remember the um security where we are currently left in the final stage of the project right here that also involves data integrity and there's one thing I want to show you and that is where are we actually adding the products to the user for example let's go in our users collection and see what that means we are adding the user to the products but not actually the other way around when a product is created how do we know which user it belongs to so let's create a new function inside of our products collection and let's call it cons sync user because that's essentially what it will do we can tell typescript this is going to be of type after change hook so after a product is created and even pass it the product as a generic this is nothing else than an asynchronous eror function where we can destructure two things from here that's going to be the request and the actual document so the product data that we are currently creating now first off we need access to the full user the con full user so the entire user object is going to be equal to a weight Rec do oops rec. payload doind by ID this time and we can simply pass this the collection we want to search in or users collection users there we go and the ID we want to search by which is the rec. user. ID and oops I didn't mean to minimize that get the current user object that way now if we have a full user and the type of oops type of full user is an object that means we have the actual user object and can perform the logic that we want on it for example we can get the products of this user the cons products are going to be equal to full user so we are destructuring them and the reason we get a squiggly line right here is because we did add the US the product field to the user but we did not generate our types from it so let's go into our CMD and say yarn generate types that's going to um you know refresh the types we have and add the products field to the users as we added it you know like 10 minutes ago and once that is done the Squigly line right here should be gone because our typescript now knows hey yes a user does have products now we need all the IDS of all the products this user currently has so that we can add the one we are currently creating right behind it so const all IDs let's call it is going to be equal to an array there we go and we can simply map over each product and return the ID so let's spread in the result so in parenthesis oops just like that the products which are optional by the way do map because they don't have to exist and for each product that this user currently has let's return a tary result so we're going to do a check if the type of product is triple equal to object that means it's the entire product object in that case we know we can return the product. ID because after all we're only after the IDS we don't care about the entire object and otherwise it's already the ID it's already of type string or if this is not true at all we can simply pass an empty array um that means the user doesn't have any products and we can you know leave it as an empt array no IDs for this user and all these IDs will actually contain the product we have just created because this is an after change hook the product has already been created now to find the one that we just created let's say con created product IDs and this is going to be all IDs do filter and we can simply filter by well we get access to the ID and the index and return from this the all IDs do index of and we're going to check if the index of ID is triple equal to the current index that we're going over in the filter and now we can simply say const data to update so what we actually actually want to change and this is nothing else than spreading in in an array all the created product oops product IDs and then adding the current doc. ID so the current product that has been created and lastly we still need to sync that with our database and in order to do that very easy we can await the rec. payload do update and call this update on our users collection users there we go the ID is going to be the full user . ID and lastly the data that we want to update is going to be an object and contain the products which are nothing else than or data data to update there we go and just like that we synchronized the user to the products once a product is created meaning in the user object we now have all the IDS of that um of products that this user currently owns and now in order to actually use that hook we can simply add another hook right down here in hook section of the products collection the after change Hook is going to be an array because we could add multiple and this is going to contain our sync user hook we have to find right above awesome and now for the security in the products the last thing we need to do for security but very important nonetheless and that will happen in the access policy so for example for the read operations who should be able to read products and the answer is you should be able to read your own products and admins should be able to read products but you shouldn't be able to read my products so let's call this the is admin or has access function and invoke that and Define that function right above so the cons is admin or has axis is nothing else than a arrow function that will return an access oops access policy and that's Nest this function so this will return another function where we will where we will be able to destructure some things later and this is going to return a regular function block let's destructure the request and right away the user and let's call this by saying colore user let's call it the underscore user so that we can say cons user is equal to underscore user which is essentially the same thing but with the purpose of that we can Now cast this type as our user type or undefined if the user is not logged in just like that now if there is no user they're not logged in let's return false you shouldn't be able to read any products and if the user. roll is triple equal to admin let's return the opposite let's return true you should be able to read all products cuz you're an admin and otherwise you should only be able to read your own products and to find that out which are your own we need to get the IDS of all products that you own so let's say the user product IDs is going to be equal to and then in parenthesis the user. products or in empty array if you don't have any products and this needs to be products there we go and then dot reduce because we're going to reduce this down to one array of Simply IDs and the logic for this reduce is honestly pretty straightforward as always we get the accumulator and the current value or let's call it the product right here and then as for the logic that will happen in the reduce well first off if we don't have a product if that doesn't exist let's return the accumulator because we can't add anything if it doesn't exist and then secondly we need to do a little check if the type of product is triple equal to string that means it's just the product ID in that case let's execute a function block where we push that into the accumulator accumulator do push and this is going to contain the product and by the way in order to get type safety for this we also need to tell typescript hey what is the type of the accumulator we can do that in here with a generic that we pass to the reduce and this is simply going to be an array of oops string there we go and close that off so typescript will be happy I know we're getting a lot of Squigly lines and that is because we didn't pass the you know starting value which is just an empty array and then otherwise if this is not a string else let's say we're going to push the accumulator do push like that the product. ID because otherwise we know it's the entire object and not just the ID and that's all the logic we can now have all the user IDs and simply return a query constraint where we can only see our own products so let's return an object so the ID of the product that we are requesting currently is in the user product ID so your products or my products it depends on the user that is currently logged in oh and by the way to get rid of this little typescript error right here we can simply say return e so the accumulator right below this statement and typescript will be super happy and all that means is you can now only read your own products and not all the products that are on this um entire project in this entire page so products of other people same thing should go for the update is admin or has access you can only update your own products and not my products and same thing for the deletion you can only um delete your own products as you want but not anyone else's products that's all this policy does and we're done with the security beautiful job let's save that and let's take it off or List hey really quick this is me editing Josh and under no circumstances ever will YouTube let me upload a video that is longer than 12 hours I rendered out this entire video that took like 2 hours uploaded it to YouTube and after 1 hour of processing it told me that you know you can't upload a video that is longer than 12 hours so I had to cut some part out and it was the mobile nav because I think it's by far the least interesting part and it allows the entire rest of the video to be normal you know I don't have to speed anything up or whatever I really didn't want to handle it that way and again I added some really good comments and I hope you understand and now let's get on with the next part now for the products page when we click on this page right here we expect to be taken to a page where all the products are listed and actually we have done all the logic in order to achieve that already we can simply go ahead in our app folder create a new folder called products and in here once again a page. TSX where we're going to create this products page so the const products page is going to be an arrow function that we can export as the default at the bottom right here products page there we go and all the logic in here as I said we already did but most importantly we need access to some search params that we can destructure in this Arrow function and give them the type of products page props what we are going to call this interface and we already know the type of this we've done this exact thing before so the interface and we can copy and paste the name here so I don't do any types in it this is only going to receive one thing and that is the search params where the as always in an object the dynamic key will be of type string and then the uh value type is going to be a string or string array or undefined and let's call that pram because we're going to reuse that in one other um you know function and say at the top right here the type Prem is just what I said the string or string uh string array or this can also be undefined if the query parameter is not passed at all and now the reason we're doing this in a separate type is so that we can have one little utility function right here let's call it const pars and this will take in a Pam of type Pam and simply return if the type of Pam is triple equal to string so if it is a valid pram that is passed into this component then let's return that pram or else we're going to return undefined so we're just making sure that it's not actually a string array so we can easily work with it right now in our products page let's see what that looks like for example the const sort what we're going to sort by which should be you know ascending or descending we can simply now parse the search prams do sort and if that is passed we get access to it right here in the sort content and if it's not passed well then it's just going to be undefined but not a string array and same thing for the category let's say cons category is going to be the parts of search prams do category what we for example pass in when we go to our um you know right here what we pass in as a query parameter when we click on the links we have just created in the mobile Navar that's what we are parsing right here on the product page so we can show the data based on it and last thing we want in here is the label that we want to show so let's say con label and we've already done this a thousand times comes from the productor categories. find and for each value that we can destructure from this Arrow function right away there we go value we're going to do the check if the value is triple equal to the category that is passed into this component as a query parameter right up here and again we only care about the label of that result and that can be defined that can be undefined so we still have to handle that case and as I said earlier all the logic for this component is totally done let's return or Max with rapper first to make sure the you know layout is good and then secondly our super nice reusable product real component that's going to handle all the logic we can pass this a title and the title will be dynamic so if we have a label which again can be a string or undefined then we're going to pass that if it's undefined we're simply going to say browse high quality assets there we go and as for the query for the data query that's going to be shown in this product real we're going to Simply Save category if that's passed good we're going to use it if not no problem at all right the limit can be 40 so we're going to show 40 products on this page more or less if you want and for the sort we're going to say if the sort that is passed as a query parameter matches or typescript expectation of either triple equal to descending or the sort is triple equal to ascending we know it's a good value we can use it so let's say sort in that case or else undefined and we're not going to sort at all and that is literally all the product logic we have we can now show up to 40 products beautifully loaded in this page without any more you know work we've done all the logic with a super nice reusable component that we can simply specify the data query 4 wherever we call it that is insanely cool all right we're almost done there are two things to completely finish up this project one is the footer to make this look very professional and the second thing is the metadata so we have or fa icon in here and not create next app and the standard verell icon that looks horrible first off let's finish up the footer very simple let's create a new file in our components called footer. TSX and one more time you know what the footer is it's nothing else an arrow function that we can export default at the very bottom of our component beautiful all right we're going to return a footer element from here to be semantically correct in HTML let's give it a class name of BG white and a flex grow of zero inside of this footer we're going to make use of our Max with wrapper to make sure the layout is perfect and matches the rest of our page and let's create a div element in here with a class name of board border DT and a border gray of oops border gray of 200 and not border 5 border t for Border top perfect okay and inside of the footer while the logic we handle in here is going to be minimal there's one thing we want to declare at the very top and that is the const paths to minimize let's call it that and this is an array of strings off paths where we don't want to show the entire footer but just a small part of it so it doesn't distract for for example that's going to be the slash verify email that's also going to be the slash sign up oop sign up slash sign up there we go and lastly that's going to be the slash sign in so basically all the authentication Pages we don't want to show the entire footer you're going to see exactly what that means right now so let's do a conditional check inside of this divette here if the paths to minimize do includes the current path name that we also need to get at the very top so the con path name once again is equal to use path name from next SL navigation and if we are on a minimized path like on the or routes then we're going to see if the uh paths to minimize dot includes the current path name and if it does that means we want to minimize this footer that means for this specific part of the jsx we're going to return null and else we're going to return a div element and we also need to close that off now this div element for the expanded big footer is going to be with a class name of flex and a justify Das Center and the only thing we're going to do in here is display our logo by importing or icons do logo and simply giving this a class name and this is going to be a height of 12 and a width of Auto to keep the perfect aspect ratio beautiful let's already see what this looks like let's head over into our main layout and this is where we can add the footer so it's automatically added to all the pages in our project we can simply put it right below the div containing our children let's insert the footer right here this can be self closing doesn't need any props and we should see well an error because we still need to Mark or footer as a use client component in order to be able to use the use path hook that's a react API that only works in you know client side rendered components let's see if that works it does there is our logo beautiful the spacing doesn't look quite right yet so let's quickly go ahead and add a another containing div to the um you know conditional check in here and wrap the div containing or logo in that let's give this a class name of um padding bottom of eight and a padding top of 16 that's going to make it look a lot nicer beautiful and below this conditional check let's do one more conditional check where the conditional check is going to be the exact same pretty much we can copy it from above paste it in this Dynamic check and lse so if we are not on a minimized off page we're going to return a div just like before this div is just for layouting purposes no class name needed and inside of here let's put one more div with a class name of relative Flex items D Center padding X of six padding y of six on small devices a padding y of eight and on large devices a margin top of zero let's open the up one more div in here with a class name of absolute an inset of zero an overflow Das hidden and a rounded of large let's open up this div and create a self closing div in here purely for decorational purposes and this will get a class name of absolute a background zinc of 50 and inset of zero a background gradient to oops to bottom right and a BG opacity of 90 perfect again this is purely for decorational purposes so we can add an area hidden label of true as an accessibility best practice right below this with two closing divs to go let's create one more div and this is going to get a class name of text Center relative an MX of Auto and a maximum width of small inside of here goes an H3 element saying become a seller beautiful so we offer users the option to get to selling page and this gets a class name of font oops font Das semi bolt and a text Gray of 900 beautiful let's save that and I did a table here become a seller there we go that looks nice and right below here let's put a P tag this is going to get a class name of margin top 2 a text of small and a text muted foreground or text color we reuse across the entire application very nice and and let's say in here if you and then and apos semicolon for the safe way to insert a you know same thing as this I don't know what it's called like a quote I guess if you'd like to sell high quality digital products you can do so in minutes oops minutes period and let also just for um nice UI sake put a link element right here taking the user by the way this comes from next SL link taking the user to our selling page so this is going to say get started and this link is going to get an HRA off SL sign-in and then as the query parameter we can pass as equals seller so question mark As equals seller so we're automatically in the seller mode of that sign in that's a beautiful side effect that we can now do because we put this in the URL and receive it from there in or sign in page very very nice and let's also give it a class name and this is going to be wh space- no WP so it will never oops no wrap so it will never you know break lines we don't want that a font Das medium a text black and and on Hover we're going to apply a text zinc oops text zinc of 900 just a tad of a different color and let's also insert a JavaScript space barx so kind of forcing a space between the dot so the last um character in the paragraph and the link element just to make things look a bit nicer let's save that see what happens that looks amazing and we can also add a little right arrow to the get started to indicate hey this is a link let's say and R A RR right arrow semicolon the HTML version of just inserting like a little arrow right here awesome and now the last thing is the legal stuff we can add at the very bottom to or footer so right before the closing of the max with wrapper let's create one more div and this will get a class name of padding y1 a medium flex a medium item Center and a medium justify between let's open this up and one more div in here with a class name of text Center and on medium a text left let's open this div up and inside of here goes A P tag saying and copy semicolon that's going to be the little copyright icon we can use and we're going to insert the current date so in Dynamic uh curly braces the new date invoke that do get full year and also invoke that and that's going to be 2023 for example so that's a really nice trick to always have the current year in the footer so it never expires and then we can say all rights reserved just like that and this ptag will simply get a class name of text small and a text muted of foreground or trusted text color beautiful that looks really really nice and below the closing ptag and the closing div let's create one more div with a class name of margin top four Flex items Das Center a justifi d Center and on medium a margin top of zero let's open this up create one more div in here with a class name of flex and Space X of8 to space them out oops not y but Space X of 8 to space them out um horizontal and in here we're going to create a nextjs link element that's going to say for example cookie uh policy or whatever that's called you know just the legal stuff that you need on a website and this link is going to Agra to a hashtag we're not going to implement the legal stuff that's not at all what this video is about and let's give it a class name of text small a text muted oops text muted foreground and lastly on Hover we're going to give it a text gray of 600 just to kind of change up the um color of it and we can copy this down once and twice and for example change the cookie policy to like terms and then also privacy policy and if you want to actually have this as a working startup in production then you could of course Implement these um Pages yourself beautiful and as you can see what the paths to minimize are for right here by default it will be the big foot with he become a seller however if we are on the all and by the way the mobile menu right here should not be visible if the screen is so large we need to fix that right now let's go in our mobile nav and to the button that is shown if the mobile menu is not open let's add a large Hidden property and that's going to get rid of the icon when it shouldn't be there beautiful and now as for the last thing we're going to do is fix the metadata on our page so we don't have the you know create next app um and it's actually your own thing and because we're very tight on time in this video I included this in the copy paste list as well it's a very simple function that just constructs the metadata for us in a very nice reusable way so let's copy this from the copy paste list and paste it into our utils dots in our lib folder just right here paste it in here and let's quickly take a look at what this does so essentially we have our deployment URL right here whatever that might be for you and we are returning a next s metadata format to tell typescript hey what this will actually do and we can simply specify things like an image or fav icon that we want to display on the page as well as like a title in description and always have the ability to overwrite these values wherever we call the construct metadata for example let's call the construct metadata in or layout. TS instead of the standard nextjs metadata that we have right here we can simply say export const metadata data there we go is going to be equal to construct construct metadata from our UTS and um you know invoke that now there might be the case where the thumbnail.png doesn't exist or the fav icon is not yet specified where do these come from let's by the way close out of all the other files they come from or public folder right here and you'll find again all the images in the public folder in the GitHub repository you can simply download them drag them over that's going to make the entire image part of this project just work for example let's drag over the thumbnail in our folder and that's going to be the preview when you share a link to this project it's going to look hella amazing out of the box and same for example with a fav icon it's also in this folder you can simply drag it over and tell the construct metadata to use that fa icon I've already done this for you you don't need to change anything just make sure it's in your public folder and then when you restart your server and refresh the page you should be able to see the updated metadata in your project so let's refresh our page we can already see that the title changed to digital hippo but the fav icon has not yet changed and I suspect that is because we have the fav icon in our app folder let's get rid of that and then let's see what happens let's hard refresh again and there is the hippo asor Fab icon and you can simply call this construct metadata anywhere where you want to change the title like in the seller page or wherever or the product page to change it to products um it's super usable and it just makes our page look so much more custom and not like the standard nextjs stuff very very nice job let's add all those changes to GitHub git add dot say git commit minus M final changes or whatever you want to commit this as doesn't matter and let's say get push and push this up into or um production environment all right and that deployed beautifully however one change I quickly made is um to go go back to the domains because remember earlier we had the remote patterns but I just couldn't get it to work and the images to actually show up in production and this works yes technically it's deprecated but it just works the remote patterns I don't really know and address I'd rather just have it work um right out of the box so change this back to domains insert both our local host and the production um URL that we have and with the production version fully deployed everything should work just as fine locally as well as in production so I've already went ahead and built out this project um locally so we can try it just going to be a bit faster than the deployed version but it doesn't really matter in which environment you're going to test this I've already tested this in the production and it works just as well there so let's go ahead into our local seller dashboard and quickly clean up all the products and I did not start the server yarn start of course we have to start that up in order for it to work and then let's go ahead into our seller dashboard beautiful and quickly get rid of all the products so we can try this again completely from scratch so let's uh Delete the one demo project or product I had for production testing and let's create a new product together let's call it solid UI that's what I called the last one as well any product description in here and let's say this will cost $9.99 all right SD category let's select UI kits and SD product files let's upload something let's select the file and I'm going to say the same image as we're going to list on the storefront just to make this a bit simpler of course in reality this would be what the user actually pays for in the end as the product stat this because we're logged in as an admin we can approve it right away if you're a user then of course an admin would have to approve it for you you know to make sure the product is actually solid and good and as for the product image let's also upload something that's going to be the same um product file as before let's hit save and by the way we can also add add another image just to make sure that the image slider also works um on the front end so let's add something else this is for another icon pack doesn't matter we just want to see the image slider working and then let's hit save of course in reality this would be like an image that fits the first one same for the product awesome now as a user let's navigate to our site and try purchasing our product the product shows up here right away and the image slider preview Airbnb style Works beautifully out of the box awesome I think we already have yeah one old product in our shopping cart that doesn't even exist anymore let's go ahead and clear it and then let's add our silicon UI we just created to our shopping cart beautiful the NAFA Works everything works and let's go into our cart let's hit continue to check out and this will work because we're already logged in and we can simply hit um check out right here to get forwarded to the stripe checkout page now inside of here let's enter or email let's enter the test card details that simulate a successful payment and all you have to do to make this actually live is to switch out of test mode in stripe which is very very simple as the card holder name let's enter anything and then let's hit pay and now we should be forwarded to the product thank you page that will check if the product was actually paid for and will only allow us a personal secure download link when it's sure that we've actually received the money beautiful and because we have deployed the project this describe web hook got called we see that the payment has actually arrived and we should be able to see a success date or we will definitely be able to see a success dat in the stripe dashboard for the web hook because the payment was successful beautiful what that means is we can now download this asset and we can see right here the actual product file that the user pays for was downloaded if we try to paste this link anywhere else same for the production version we would have to sign in first and then the logic would check if this is or product and only if we actually own the product it would actually allow us to download the product um just for security you know very very nice let's log out that's going to redirect us to the signin page right here and super super nice that is the project there's so much involved in this beautiful animations architecture thinking how do we design this so much stuff involved and I really hope you learned a lot following along with me in this video by the way I'm going to put the email the received email we've received in the inbox right on screen here so I don't have to show you my entire inbox but you can see the email yourself if you followed along with this project it will arrive in your inbox and summarize everything that we just bought in or order amazing job again I really appreciate you following along and I truly hope you learned as much as I promised in the very beginning of the video cuz I like to keep my promises and you're left with a super nice complete project I really hope you enjoyed this video that's going to be a for this video and I'm going to see you in the next one have a good one and bye-bye
Info
Channel: Josh tried coding
Views: 169,399
Rating: undefined out of 5
Keywords: nextjs, next, next 14, nextjs 14, react, tailwind, tailwind css, tailwindcss, trpc, trpc nextjs, payload, payloadcms, payload cms, josh tried coding, joshtriedcoding, full course, digital marketplace, e commerce, e-commerce, ecommerce, react ecommerce, nextjs ecommerce, express, self host
Id: 06g6YJ6JCJU
Channel Id: undefined
Length: 720min 0sec (43200 seconds)
Published: Mon Nov 20 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.