Build a SaaS AI Platform with Next.js 13, React, Tailwind, Prisma, Stripe | Full Tutorial 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey there my name is Antonio and welcome to the newest video on my channel in this tutorial I'm going to teach you how to build a full stack software as a service AI platform and you're going to learn how to build not just one but five different AI tools so let's go ahead and let me show you exactly what this amazing application is capable of let's start with the image generation I think that's one of the coolest AI models in here for example a horse in Swiss Alps and I can select the amount of photos I want to generate let's pick four photos in the lowest resolution so they load fastest we have a prompt that genius the name of our app is thinking and after a couple of seconds you're going to see what amazing images this AI will come up with look at this absolutely amazing now let's do the same thing but for video generation right here I'm gonna write the clown fish swimming around a coral reef and I'm going to click generate again and after a couple of seconds you're gonna see what this AI will come up with look at this absolutely amazing let's play the video to see what it looks like can you believe an AI has generated this absolutely amazing now let's check out the conversation model right here but before I write anything here I just want to quickly bring your attention to my lower left corner right here where it says that I've used them two out of five of my three generations so what does that mean well let me remind you this is not going to be your average AI tutorial we are going to build a full stack production ready software as a service platform meaning that I'm going to teach you how to create a free tier as well as a monthly subscription tier using stripe for your users so let's go ahead and let's use up this the rest of these models right here so we can fill up these three generations I'm gonna ask the conversation model what is the radius of the sun right here we have a similar thinking prompt right here and there we go look at the answer amazing and look at this now it says 3 out of 5 five free Generations because we just used one great now let's go ahead and let's fill this up to 5 out of 5 so I can show you what happens then I'm gonna test the music Generation Now for example piano solo right here I'm gonna press enter and after a couple of seconds you're gonna see that this AI model can actually come up with audio files look at this absolutely amazing you can play it you can change the volume you can download the file change the playback speed amazing I'm not gonna play it just in case someone decides to copyright AI music in the future and now let's take a look at the last Model right here which is code generation I'm gonna go ahead and write a model using react Hooks and Tailwind I'm going to press enter and after a couple of seconds you're gonna see that this AI model is capable of writing code for you look at this not only did it give you a nice explanation it also wrote the code in markdown so it's nice and easy to read absolutely amazing but what happens now that we've used up 5 out of 5 of our three generations but let's go ahead and let's try to generate another image for example I'm gonna write a shark in DC and I'm going to press generate and look at this it's blocking us from using the AI That's because you're going to learn how to build this we are going to protect our free tier so the users who are not subscribed can only use the AI model five times after that they have to upgrade so I'm gonna click upgrade right here and that is going to redirect us to the stripe checkout page right here I'm going to write a fake credit card number some more fake information I'm going to write my name right here and I'm going to click checkout and in a couple of seconds we're going to get redirected to the settings page there we go look at this absolutely amazing now in our settings it says that we are currently on a Pro Plan and we can of course manage our subscription from here if we want to cancel it so let's go ahead and test how that looks right here again this time it's not going to redirect us to the checkout page but to the billing page so I can see all of my information I can see how long this lasts how much I've paid and I can of course cancel my plan but now I'm gonna go back and I'm gonna test if this subscription actually works let's go ahead and let's go into an image generation and write I don't know a pretty sunset now let's go ahead and generate five photos right now we have a similar prompt that genius is thinking and you can already see this time we are not blocked by the premium model prompt instead it successfully generated a pretty sunset beautiful let's try the conversation again what is the radius of the Earth there we go we are not blocked again so our subscription model is actually working perfect and what would a software as a service be without customer support in this tutorial I'm also going to teach you how to build beautiful customer support using crisp you can go ahead and write any problem you have just like this and that is going to appear on the crisp dashboard in real time so you can respond back to your customers and we are also going to have authentication this time using clerk which I am extremely excited to say is this video's sponsor and also my first sponsor ever but that is not all because every software as a service also needs a beautiful landing page so I'm gonna teach you how to create this as well so without further Ado let's get started so let's get started and let's set up our application I'm going to follow the chat cnui setup instructions you can find those by Googling chat cnui clicking on the website right here and just go ahead into the documentation and click installation after that you have to select next JS or whatever you are using but in this tutorial we're going to be using next.js so that's why I have this opened right here so the First Command we have to run is the create next app command this command is actually not related to chat cnui this is the next 13 command but it is written in this tutorial because this is a setup for next JS so here on the left side I have my visual studio code and I'm going to go ahead and I'm going to open my terminal and I'm going to go ahead and write npx create next app at latest and I'm going to name this AI Dash SAS you can name it whatever you want and go ahead and give it the follow with x dash dash typescript dash dash tailwind and dash dash s lint like that or you can simply press copy here and select npm but I'm going to write it out so you can clearly see what I'm doing and now we can go ahead and press enter and I got a prompt that I need to install the newer version of next app so go ahead and just press yes if this is the prompt you have there's a chance you just don't get that prompt at all and you immediately have this question right here so the first thing it's asking us is whether we want to use the source directory for that select no for the app router this is very important you have to select yes for the import areas you can select no that is going to keep the default at sign and there we go now it's starting to install our application so just wait a couple of seconds and you will have your app ready after a couple of seconds you're gonna get a message similar to this saying success and that it has created a project called AI Dash SAS or whatever you named it in your command at this following location so I'm going to click open right here and I'm going to select AI SAS right here and there we go now I'm inside of that folder but before I start this application I want to run the rest of the commands followed by the chatsea and instructions because chat CN is the library we are going to use for styling so let's go ahead and let's open the terminal again right here and I just have to quickly update this so I'm going to pause the video all right now I have to run the following command npx chat cn-ui at latest in it so let's go ahead and run that npx shadesian Dash UI at latest in it like this and now we're gonna get a set of questions so would you like to use typescript for that we select yes because we initialize our next 13 project with typescript now it it's asking us for the style I'm going to use the default style and I highly recommend you do the same you can choose New York if you want so use arrow keys up and down to select the option if you select New York it can look slightly different you can still follow the tutorial but your icons are going to come from a different package than that what I will be using in the tutorial so if you don't want that confusion just choose default and press enter now for the base color you can select any color you want it doesn't really matter it won't affect the project but it will look a little bit different than mine so for mine I'm going to choose slate like this is is asking us where are Global CSS file is and this is the correct answer app slash Global CSS now would you like to use the CSS variables for colors select yes as well it's asking us where is our Tailwind config it's at the root folder named exactly like this so press enter for that and configure the import Alias for components this looks fine I want it to look like that and you can press enter for this as well and lastly it's asking us whether we are using server components since we are using the app router the answer for that is yes and now it's asking us to confirm all of those options so we pretty much didn't change absolutely anything you can just press enter through all of these options and now just press Y and now it's going to initialize chat cnui inside of your project there we go just like that it has added uh chat cnui and now we can go ahead and run npm run Dev and your project is running on localhost 3000 so I'm going to click on this link right here and I'm going to show you how your project looks so I'm going to expand the screen and you should see something like this so now let's go ahead and let's quickly clean up our application because there is a lot of stuff inside of here that we don't actually need so I'm going to collapse everything and as you can see we have the app folder where all most of our routes I mean all of our routes are going to be we have the components which are currently empty but this is where the components from chat CN are going to appear we'll have the lib folder which was generated by chat CN UI and we're going to use this to merge Tailwind classes I'm going to explain that when we come to that part and the rest is just the configuration for the project so let's go inside of the app folder and let's take a look at what we have here so we have two important files page and layout.dsx our layout.tsx can stay pretty much the same for now we can change that later but now in page.dsx what I want to do is I want to clean everything inside of the return function so go ahead and go to the top all the way to the bottom and just clean everything in side and you can also remove this import and you can just write a paragraph hello AI SAS like that and there we go look at this it's right here hello AI says but the font looks kind of small let's see if we can increase it using Tailwind go ahead and add a class name text-6l like this there we go now the text is much much larger and let's try and change the color text green Dash 500 there we go so our Tailwind is successfully set up before we move on to the next part I want to show you how easy it is to add components from chat CN to our project so I'm going to go back in here I'm going to zoom out a little bit and I'm going to go all the way down to the components in the sidebar and one of the components we are certainly going to need is the button component and right here is the command we have to use to edit but one cool thing about chat cnui is that you can also manually add it if you want to so you don't have to use the CLI why am I choosing chat CN and why not chakra or material UI kit or all of these other amazing CSS Frameworks well the reason I'm using chat cnui is because it works in a different way chat cnui is not a component Library instead it is just a a I don't know exactly what is the correct phrase for that but what it allows us to do is to keep the components inside of our components folder usually when we use material UI or chakra we have no access to the components we can import them and we can edit them using the props they've given us but we have no access to the actual code that there that the way those components are working they are in node modules but with chat cnui they're going to be directly in our components folder so we can modify them in any way we want so let's test this out I'm going to go ahead and click copy here so I advise you that every time I am on the chat cnui page I advise you to do the same because there are some parts that we cannot install as easily as this but we're gonna have to copy a code snippet so I just want you to be on the same page as I am so it's easier for you to follow this tutorial so I'm going to copy this npm Command right here and I'm going to go into my terminal right here you don't have to shut it down but I will because I'm using only one terminal and I'm just going to expand this a little bit and I'm going to paste this command so the command is npx chat cn-ui at latest add button and I'm going to press enter like this and now it's asking us whether we are ready to install that so just press Y and there we go it's installing the button inside of our project I'm going to run MTM run Dev again and let's see if we can actually use this button now so what has happened in our project if I refresh right here not much has changed we still have the hello AI SAS but let's take a look at our components folder right here you can see that now we have a new folder inside called UI and inside of that folder we have a button.tsx and now if you look inside you can see that we have this amazingly uh strictly typed written and area of written button component so this is a much more advanced button than what would usually be if we were writing it ourselves and I think that's absolutely amazing and what's cool about it is that you can rename absolutely anything you want inside so whatever you don't like you can rename so you have complete ownership of your component system that's why I really like chat cnui so we have just added this button and technically we never have to look at it again you can just close this all together and let's go back into our home right here and I'm going to replace this and I'm actually going to use that button so I'm going to write button like this and I'm going to import it from add slash components slash UI slash button like this and make sure you destructure it like that and I'm going to write click me like this and I'm just going to increase my zoom a little bit so you can see better there we go look at the button right here and let's see how easy it is to change the variant for example I can go ahead and say destructive like this there we go now it's red and we can use that for when we are deleting some stuff and I just want to show you how cool it is for example you can go back into components UI button and this is the destructive style that you're using right now let's say you don't like the word destructive you can go ahead and change that to Danger so you don't have to do this I'm just demonstrating now you can change that and you can see now our code is not working and you can see I have a typescript error here so I can change it to Danger right here and there we go now it works so that's what this component Library well it's not a component library but that's what chat cnui offers us I think it's really cool that we have this kind of customer custom ability here so just make sure you bring this back to destructive I'm not going to change the button component in any way but I'm just going to bring this back so destructive like that and I just want to show you that you also have a couple of other props like size so you can change the size to large for example and now the button is a little bit larger like that great so this is how we are going to add components in our project and then we're going to style them and we're slowly going to create our UI great start you did an amazing job and now we are ready to go ahead and start developing uh some other stuff let's continue with the structure of our project so I'm going to go back inside of my app folder and I'm gonna go inside globals.css right here so everything that you see right here has actually been generated using chat CN UI but this is fine I just want to add one thing here and that is the height for our HTML and our body so let's go ahead and write this HTML comma body and root element like this and I just want to write height 100 and that's not going to change anything right now but it is going to help us in the future great and now I want to go back inside of my layout.tsx file right here and I want to change the title of my application to genius and I want to change the description to AI platform like this but with the capital AI like this and now you can see that my title right here has changed to genius previously it was was let's see it was create next app but after we do the changes it now says genius great so what I want to do now is I want to organize my routes in a specific way but before I do that I want to give you a quick explanation of how routes actually work inside of next so right here we have this page.thesx file but how do we know that that is located right here at the slash well that's because if you have a page that CSX plainly put inside of your app folder that is going to result in the URL slash or just your domain like this but what happens if I create a new folder for example called test and inside I create a new file page.dsx I'm going to quickly go ahead and write test page right here and I'm going to write a div test page like this so make sure you have the test page and Export default fast page at the end it's important that you use export default now how do I find this page where is this well it's located at slash test in your url right here so now we know that every folder we create inside of the app folder which has the page.tsx inside of it is a route on its own but there is also a way that you can use folders and page.tsx without actually affecting the URL this is a cool feature because sometimes we want to organize our routes in folders but not necessarily change the url structure so for now I'm going to go ahead and I'm going to shut down my application because we're going to do some changes in here so I recommend you shut it down as well so let's go ahead now if you refresh nothing's gonna work but that's fine first thing I want to do is I want to delete the test page great and what we're going to do now is something called route groups so right here in my right side I have the explanation for that so route groups are is especially named folder which does not affect the URL so you can go ahead and read more about that just type in route groups next JS and you're going to find this exact documentation you can see the convention the convention is to write the folder inside of parenthesis that way it's not going to be affecting the URL so you can see that right here they have a folder called marketing but the URL is just slash about nowhere in this URL do you see the marketing that's because they are just using it to structure your files and it's exactly okay what I want to do as well so what I'm going to do I'm going to go back inside of my app folder right here and I'm going to go ahead and I'm going to create a new folder called dashboard like that and inside of that dashboard well I made a mistake here right now this is affecting the URL so we have to rename this right click go ahead and rename this and make sure you add parentheses around the dashboard like that now it's not affecting the URL so what I want to do now is go ahead and create another route group inside called routes like this what this is going to hold is all of the routes for our AIS so inside we're going to have a route for image Generation video generation our general dashboard code generation and settings so inside we can actually go ahead and write the route which is going to affect the URL this route is going to be called dashboard like that so this one won't matter for the URL neither will the routes but this one will and inside I'm gonna go ahead and create page.tsx and what I'm going to do is I'm going to go back to my old page.vsx that is inside of the root of the app folder so go inside of here and just copy everything inside and paste it in this new dashboard page.phsx right this and you can rename this to dashboard page like that so just make sure you export the default you will sometimes see me using the function and sometimes you will see me using the uh well this const dashboard page the arrow function and I will export default dashboard page it really doesn't matter it's gonna have the same effect it's just your preference of course there are differences between Arrow functions and plane functions but that is not the topic of this tutorial but just to ease up some confusion it doesn't really matter what you used but you can always follow exactly what I do so you have the same result so for the dashboard page I'm going to write const dashboard page Arrow function and Export default the dashboard page the naming of the component does not matter for the URL you can name it whatever you want but I like the conversion of using the entity name and then capital P page beautiful and now that we have this I actually want to remove this root page because we don't need it so you can move that to trash great and what happens now well we have a problem because this is a route slash dashboard but we don't have our slash route we don't have the root so let's go ahead and create that for that I'm going to create another route group called Landing like this that's going to be our landing page if you remember from the intro of this tutorial and inside go ahead and create another page.dsx I'm gonna go ahead and create the landing page like this div landing page and I'm gonna write in parenthesis here unprotected so every user whether they are logged in or not are going to be able to see this landing page great so now that we have these two let's go ahead and let's run our project and let's see if we made any mistakes or if everything is working as expected so I'm going to refresh this right here and I am at slash test so I got a 404 so I'm going to go back to my root and I should see the landing page right here perfect great so how do I see this dashboard let's see this is not affecting the URL routes is not affecting the URL either but dashboard is that means that all I have to do to visit this page I'm going to change this now I'm going to write a paragraph saying dashboard page like this so in order to visit this page all I have to do is write slash dashboard like this there we go now I have the dashboard page but on the root I have the landing page and I just want to add here protect it so only authorized users and you can remove the import per button We're not gonna need it now so only users who are authorized are gonna be able to see the dashboard page for now of course that is not true because we have not added authentication so that's what we're gonna do in the next part but for now this is great you've done a great job so far so let me just recap what we've done we have removed the page.tsx inside of our app folder and we've moved it inside of Landing page.tsx but because Landing is inside of parenthesis that means that we are using the route group convention right here you can see it's in the parenthesis meaning it's not affecting the euro L it's just a folder where we decided to keep our root page for easier structure and then in dashboard we used a bit of a different structure so again we used route group so it's not affecting the URL but we did not put page directly inside instead we created another route group called routes that's because besides dashboard we're going to have many folders inside of this route and inside of dashboard right here we're going to have another file called layout so this way we have structurally separated our routes from our other stuff don't worry if this is a bit confusing It's Gonna Get Easier and easier the more you work with next now it's time to add authentication to our project so before we start anything what I want to do actually is shut down our application so we don't have any hot reloading errors so I'm gonna shut it down and I recommend you the same great and we're gonna add authentication using clerk so you can either visit clerk.com or visit the link in the description or just use clerk elf right here and it's the first result on Google great so go ahead and find the sign in or sign up button depending on whether you already had an account so I'm going to guide you through this process you can use GitHub or Google or regular registration I'm going to use Google for this one so I'm going to pause the video a bit and register there we go now you're gonna see a screen similar to this and now it's time to name our application so I'm going to use AI Dash SAS for this right here and you can play around and add as many providers as you want but for now I'm only going to enable Google and the email address but feel free to enable as many of this if you want to play around clerk is truly amazing when it comes to how easy it is to build with it now let's go ahead and click create application right here and now you can see that we have a documentation to how to start implementing this in our app so right here we have dot environment.local right here that we have to copy and put in our environment file this file is going to hold our secrets which clerk library is going to use to enable authentication in our project so go ahead and click the copy right here go ahead back inside of your project and I want to create a new file instead of dot environment.local I'm going to use Dot environment this is because we're gonna need dot environment for Prisma so go ahead and just paste that inside so make sure you have the next public clerk publishable key and clerk secret key basically what is written in this documentation right here and make sure you save your dot environment file great and now just make sure that next JS is selected right here and we're gonna click continue in docs and what I love about clerk is that they have support for the app router that gives me confidence that it's going to work in this project and I already used clerk in my last e-commerce project and if you saw that then you know how easy it is to implement Clerk and what amazing UI components it offers they are beautifully designed and they fit almost every single project so let's continue and let's follow this documentation and see exactly what we have to build so first it's telling us to install clerk next.js so I'm going to copy that and I'm going to go inside of my terminal and I'm gonna install that package so let's go ahead and write npm install ad clerk slash next JS like this and after a couple of seconds our package is going to be installed great now let's continue and follow what the documentation is telling us to do so it's telling us to set our environment keys but that is something we already done so we have them right here so we can skip that and go to the next step which is Mount the clerk provider and this is what I was talking about so if you're using Pages for any reason you can just click pages and follow that but we are using the app router so make sure you're following the documentation for the app router so what we have to do is we have to import clerk Provider from adclerk in xjs so I'm going to close the terminal for now and I'm going to go back instead of my app folder inside layout.dsx right here because that is the component they are looking at here so let's go ahead and copy this line and let's go ahead and import it right here there we go clerk Provider from add clerk slash next JS and what we have to do now is we have to wrap our entire application inside of that clerk provider let's go ahead and write clerk provider like this and at the end like that and I just want to indent everything inside beautiful and now let's go ahead and add our middleware file so go to your sidebar right here I'm going to close everything again make sure you're at the root and just create a new file called middleware.ts and go ahead and copy this entire file from the documentation and just paste it here there we go beautiful and now what we have to do is we have to create our sign up and our sign in page and they have the exact steps to do that right here so let's go ahead let's go inside of our app folder and let's go ahead and create a new folder I'm going to go ahead and create another route group here called alph like this beautiful and inside I'm going to create another folder called routes because we're going to have two routes the sign up and the sign in and I'm also going to add a layout inside so let's go ahead and create a new folder inside of this route group routes and we're going to call this sign Dash up like that and then inside of that we have to create another folder go ahead and open square brackets then again open square brackets inside so double square brackets this is another next 13 convention that allows a clerk to have all the necessary routes it needs for authentication and inside you're going to spread using three dots sign Dash up like that and then go ahead and create a new file called page.dsx there we go just like that and then you can go ahead and copy this example and paste it inside beautiful so it's that simple to create a sign up page great and we have to do the same thing for the sign in so you can just copy this entire folder paste it inside routes so now you have sign Dash up and sign Dash up copy just rename this the dash sign in like this and inside don't forget to rename this folder as well like this Dash sign in beautiful go inside of that page.thesx and you can go ahead and copy this example and paste it right here I'm having an error here but that is because of my visual studio code so I'm going to press command shift and P and I'm going to write reload window and I'm going to press enter so this has refreshed my visual studio code because sometimes cache gets in the way and shows us errors that aren't actually there you can see that now everything works just fine so let me show you the exact layout right here so I created a new folder called out which is a route group meaning it will not affect the URL inside I created another folder called routes again in parentheses so not affecting the URL and then we have the sign in and this sign up with a special convention folder inside of it so it has all the necessary routes for clerk there we go that's how easy it was to add our sign in and sign up pages and now what we have to do is add some more environment variables so clerk knows where to redirect us after we log in or where to go in general for sign in and sign up so go to this section called update your environment variables and I'm just going to copy this right here I'm gonna go back in my DOT environment file and after this too I'm going to paste this so make sure you have the sign in url sign up URL after sign in url and after sign up URL and one thing I want to change immediately is that after we log in where do we want to be redirected let's take a look so we said that our landing page is going to be unprotected but our dashboard page is going to be protected so obviously what I want to do in my environment file is that after user has logged in I want to redirect to slash dashboard like that we don't want our user to stay on the landing page beautiful how do we know that this is slash sign in and that this is slash sign up and can we change it of course we can but it has to match exactly what you write in your out folder so if you want to change this to login you can do that but make sure you change the dot dot login as well if you want to change this to register you can do that as well and make sure you change that and it has to match this to environment variables if you're following these tutorials you don't have to do anything like that just follow along beautiful and now I'm going to show you a really cool thing I want to go back inside of my middleware so go inside middleware.ts and I'm going to show you how easy it is to add a public route so right now all of our routes are protected but I want to add public routes like this and I just want to give it a slash meaning our landing page is now unprotected meaning that both logged in and logged out users can visit it so let's go ahead and let's run our application and see how that looks right now so I'm going to go ahead and write npm run Dev again and I'm gonna go and visit my localhost 3000 and I should be able to normally visit the landing page there we go but if I go to slash dashboard what's going to happen is that I'm redirected to sign in right here beautiful so it fully Works what I want to do now is I want to Center this so it's uh not right here in the corner so let's go ahead and do that now in order to do that we're gonna have to go back inside of our I'm going to go back inside of our ad folder inside of our out right here I'm going to close the routes and inside of the out folder create a new file called layout.dsx like this go ahead and write uh Health layout like this and it's going to accept children and those children have a type of children react dot react node like that and inside I'm going to write a div and I'm going to render children inside and now our error has gone away and all we have to do the 10th of it is class name blacks items Dash Center justify Dash Center like that and you can give it an H dash pool as well and now you can see it's nicely in the middle beautiful so this now works for both sign in and for sign up because we have added a common layout file which is going to affect all of the routes inside we're going to do a similar thing in the dashboard so that's why I separated this in a folder called routes so there's a very clear distinction that the routes are only inside of this folder and this layout thing is inside this main group folder right here perfect so let's go ahead and I want to go to the landing page right now and I want to show you how to add a button which is actually going to redirect to our authentication so let's go ahead and just below that I want to add another div and I'm going to add a button like that and there's a couple of ways you can do this so I'm gonna go ahead and write uh login for example here and I'm gonna wrap this in a link from next slash link like this and I'm going to add an href slash sine Dash in like this so now when I click here you can see that I am on the sign in page great I'm gonna go back and I'm gonna copy this entire thing like this and I'm going to change this to sign Dash up and this is going to be register great so now we have the login and the register so once I click login you can see I'm in sign in when I click register you can see that I'm in the register beautiful and one more cool thing I want to show you while we're already here is that you can play around and customize stuff as much as you want inside of your clerk for example click on customization right here go and check out The Branding so you can see that once I click here it says sign in to continue to AI Dash SAS but we said that our name is going to be called genius so let's go ahead and name it genius and just click apply changes and now once I go back and change it here you can see that it says to continue to genius the name of our application that's how easy it is to work with the clerk and you can play around and change whatever you want you can look at these hosted Pages if you want to you can change the colors a bunch of stuff you can play around with it as much as you want but now let's go ahead and let's actually show you how it looks when we log in so I'm going to click login right here I'm going to use Google and since I have only one account I'm immediately going to get redirected to the dashboard page so this is now fully working we are officially logged in that's how easy it was to use Clerk and let me show you a cool component that we can use now so let's go inside of the app folder inside dashboard routes dashboard page.esx and I'm going to go ahead and write this entire thing inside of a div and just below this I'm going to add a component from the clerk Library called user button so user button from add slash Clerk next.js and look at this now we have the currently logged in user and from here you can sign out and yes right now we are redirected to this page but I'm going to show you how to control where we are redirected once we sign out so let's go back to localhost 3000 let's log in again using Google we are back here and I'm gonna add after sign out URL to be slash and now if you try and refresh and sign out again you can see that we're back on the landing page and if I go to slash dashboard you can see that I'm uh triggered I've triggered the sign in button and once I log in I can freely visit the dashboard page so that's how easy it was to use clerk you can go ahead and play around and see everything it has and they're constantly updating I really like using Clerk and I'm very happy that they're sponsoring this video because I want to use clerk regardless if they were sponsoring this or not you saw that in the e-commerce video it really helped us save some time when I want to focus on more important stuff than Authentication let's continue and let's start creating the layout for our dashboard routes so first thing I'm going to do is just zoom in the code a little bit so you can see a bit better great now what I want to do is I'm going to close everything I'm going to go inside of my app folder inside of dashboard right here and inside of there so not inside routes but inside dashboard I'm going to create a new file called layout.dsx like this and I'm going to go ahead and I'm going to create a component called dashboard layout just like this and the props is going to have is children and we're gonna write the types for those so children is a type of react.react node like this perfect and inside of this dashboard what I'm sorry inside of this dashboard layout what I'm going to do is I'm going to create a div and I'm going to give this a class name of H dash full like this and I'm also going to give it a class name of relative because we're going to use some absolute elements in the future so it's going to be very useful to have this great and now what I'm going to do is I'm going to create a div for our sidebar so let's go ahead and let's write class name hidden by default so on small devices we are not going to show this sidebar we're going to have a toggle button which is going to open that in a sheet component also known as drawer maybe you've heard it like that and let's give this an H4 let's go ahead and give it an MD of flex so it's going to be hidden on mobile devices but when we come to a medium screen we're going to use flex that's mean it's going to be visible using the flex element now let's go ahead and let's write the following MD is also going to have a Plex Dash call like that and MD is also going to be fixed on the side let's also write MD in set Dash Y dash zero and let's give it the Z index of 80 like this sorry like this and let's write VG Dash gray Dash 900 like that for now great and now inside what you can do if you want to it's just a div like this and write hello sidebar like that and now you can see that we have a sidebar which has this BG gray color right here and if you collapse your screen enough you can see that it disappears because on mobile it's going to be hidden great so just keep it open for now make sure you are not if you're zoomed in it's also going to disappear so make sure you're not too zoomed in and make sure you can actually see this sidebar because that's the way we want to develop for now of course we're going to replace this div with the actual sidebar component when we get to creating that but before we do that I want to create a main element right here and I'm going to leave this a class name on medium devices I'm gonna go and give it a padding left of a 72. that's because this um this element right here something I forgot to put here it seems so let's see I put MD fixed and MD inset but one thing I forgot to do after MD Flex is give it a width so go ahead and just after this MD Flex give it an MD W Dash 72 like this so great now you can see that it has filled up more of my screen so these are the classes needed for the container which is going to hold the sidebar hidden H4 mdflex this is important MD w-72 MD Flex code fixed and the positioning for it great and now it looks much better and now inside I'm giving this main content a padding of left of 72. so if I write hello content inside like this you can see that it's not overlapping with the sidebar if we don't have this class you can see that it disappears it's behind the sidebar so this way we push it for the amount of 72 which is the same value of the width of the sidebar meaning that it's always going to be visible because it is going to be pushed further than the width of the sidebar great and now what I want to do is inside of this main is actually render the children like this so now you can see that we have this dashboard page rendered inside if you're not seeing any of this just make sure you're on the dashboard page that's very important great now I'm going to replace this hello content with a navbar component and if we save of course we are going to get an error because navbar component does not exist so let's close our app folder let's go inside the components and I'm not going to do this inside the UI folder I'm going to reserve the UI folder for chat CN components so this is going to be our custom component so let's create a new file then let's name it navbar.psx so just make sure it's not inside of the UI folder it's still going to work if you put it inside and if you want it you can do that but I'm gonna do it like this and let's go right and right nav bar like this it's not going to have any props let's give this a class name of flex items Dash Center mp-4 like that and for now all I'm going to do uh for the toggle button for mobile sidebar is going to be a button so I'm going to import that from dot slash UI button but I'm going to replace this from slash components because I want to use this Alias rather than relative Imports I think they look nicer both is working so you can also use of course dot slash UI button but I just prefer it this way I want to be consistent and I'm going to use the menu icon from Lucid react if you're wondering where did this cut package come from we got it when we installed chat CN UI and that's what I mentioned in the beginning if you chose a New York style instead of default style you you will not have lucid react installed you're going to have Radix icons so if you want to use that go ahead and find the equivalent to Radix I icon for that but if you follow the tutorial exactly as I did then no problem you have this and you can just save the file and in this button I'm going to give it a variant of ghost and I'm going to give it a size of Icon and a class name is going to be and the hidden like that okay so we have the basic nav bar right here ready so let's go back to our dashboard layout and now we can go ahead and import this from add slash components in navbar like this and now when I save this uh we cannot see anything but if I Collapse there we go you can see that I have a toggle button which later is going to open a mobile sidebar but on desktop it's not visible because we don't want it to be visible great now I just want to quickly clean up my dashboard route so go into the dashboard folder into routes dashboard page.tsx and remove this user button and remove the Imports so a very clean dashboard page like that so now we can go back into our components inside navbar.dsx right here and we're gonna actually add that uh user button here instead so go ahead and open a class name here sorry a div with a class name of flex W Dash full and justify Dash end and go ahead and write the user button from add slash clerk next JS like this so now you can see that our user button is in our nav bar right here at the top perfect and don't forget to give this a prop of after sign out URL to go to slash perfect Let's test it out so if I sign out I'm back oh I did not refresh my apologies so I'm going to go back to localhost I'm gonna I'm gonna refresh this time I'm going to click login I'm going to use Google login and if I try again there we go everything is working fine so just log in and make sure you're on the slash dashboard again see you can continue developing this great so now that we have our navbar setup what we're going to do is we're going to actually create this sidebar component so let's go ahead and do that I'm going to replace this div saying hello sidebar with an actual sidebar component right now of course this sidebar does not exist so we are gonna get an error we're gonna create our sidebar in the very same place where we created our navbar so I'm going to close the app folder and inside of the components folder I'm going to create a new file called sidebar.tsx like this perfect so let's go ahead and let's start developing this the first thing I want to do is I want to mark this as use client because this is going to be a client component since it's going to have some route names and a bunch of other stuff like that great what I want to do now is I want to well let's actually just start developing so we'll slowly change things up how they need to be done so go let's go ahead and write this shorthand for sidebar like this we're going to leave the props empty for now my apologies I added this okay let's go inside of this return function let's write a div right here let's go ahead and write class name space Dash Y dash 4 by 4 Flex Flex Dash call H dash full BG Dash open square brackets and write a hex code of 111827 so it's just a specific background color that I like and text white like this and inside I'm going to write hello sidebar component like this so make sure you have at least this and then you can go back into app folder dashboard layout.ksx and you can actually import this sidebar component from add slash components sidebar the same way you did with navbar right here perfect let's go back in our sidebar component and let's continue developing this so I'm going to remove this text hello sidebar component and what I'm actually going to do is I'm going to create a div with some spacing so let's go ahead and give it a padding on both sides of three and on top sides for two and flex dash one like this and inside I'm going to use a link component from next slash link like this let's go ahead and inside I'm going to write our logo text let's give it an hrep of Slash dashboard so when users click on that they are gonna get redirected back to the dashboard so once I click here well we are already here so no effect seen but it's going to be an effect when we have more routes and let's give this some class names so class name here is going to be Flex items Dash Center and pl-3 and mb-14 like this and instead of just a logo let's go ahead and write a div here with a class name of relative w-8h-8 and mr-4 like this and inside I'm going to use an image component from next slash image like this and let's go ahead and give this a fill property an out property of logo and the source is going to be slash logo dot PNG if we save we're gonna get this broken image because this image has not been added to our project we're gonna add images inside of our public folder right here so to find the logo you can either find anything you want any image you want or you can visit my GitHub repository link is in the description go into my public folder and just find logo.png and you can just save that file like this and drag and drop it inside of public and make sure it's named logo.png not logo one like it was in my case and now when you refresh there we go you have a beautiful logo in the corner perfect and now just outside of this div right here holding our image so below that go ahead and open an H1 element genius like that and let's give this some class names so I'm going to style it by saying uh text Dash to Excel and font Dash bold like that but one thing I want to do is I want to change the font of this text but I want to keep the same font that it already is on the entire website so I only want to change the font for this one H1 tag so in order to do that I'm going to use uh a specific package which we already have from next so let's go ahead and write const coppins which is going to be the name of the font I want to use oh my apologies it's actually going to be Monster Rat is going to be Monster Rat which you can import from next slash font slash Google like this so make sure you destructure it and import it from next slash font slash Google like that now let's go ahead and give it a weight of 600 because we want a bold model and subsets of Latin like this I'm just going to collapse this so you can see it in a better way there we go so these are the properties of the Montserrat constant right here perfect and what I want to do now is I want to append that to this class name right here so I'm going to do that using the CN Library which we have in our utils right here which we got from chatsy and UI so let's go back in our uh sidebar where is it it's right here okay and I'm gonna modify this so instead of just the strings what I'm going to do is I'm going to wrap this inside curly brackets like this and I'm going to add CN and I'm going to import it from add slash lib Slash use like this so great now that we have that I'm just going to separate it because I like to keep my personal inputs below and Global inputs at the top like that and how CN works is that it accepts class names inside of its parenthesis but you can add both the default class themes like this and then you can add a comma and in here you can add conditional class names or specially generated class names like this constant right here so I'm going to write monsterat which is this class with this constant that we added right here dot class name like this and once I save you can see that my font has changed perfect so why am I using CN why not just template literal strings or backticks as maybe that's how you know them well that's because this CN library is a specially made Library which uses Tailwind merge and clsx to ensure that there is a proper way we are adding additional Dynamic class names to Tailwind elements meaning that it's going to override them if they already exist here and it's not gonna create conflicts great so now that we have a debt we can go ahead and continue uh rendering the actual routes inside of here but before we render the routes we actually have to generate an array which is going to hold all of our routes so let's go do that here at the top const routes is equal to a couple of objects in size so make sure it's an array which holds object the first object is going to have a label of dashboard like that it's gonna have an icon of layout dashboard which you can import from Lucid react right here href is going to be slash dashboard like that and we're going to have a color of text Sky Dash 500 you're gonna see what color is going to be used for uh in a second great so I just added my first item here we're going to add some more items but before we finish this I just want to render this out so you can see how it's going to look like let's go back here I'm going to expand this as much as I can before it collapses to Mobile so like this so you can see more of the my code alright so outside of this link component right here create a new div with a class name of space Dash Y dash one so each element inside is going to be spaced evenly using this class name and inside I'm going to write routes which we just created dot map we're going to get the individual route right here and I'm gonna open an immediate return function we're going to wrap everything in a link component which we already have imported right here and it let's go ahead and let's give it an href of route.href and let's give it a key the same route.href because it's guaranteed that that is unique perfect sorry not clay but key like this great and now inside of that let's write a div with a class name of flex items Dash Center plex-1 and let's go ahead and render route dot whoops sorry route dot icon which is a self-closing tag and we're going to give it a class name open curly brackets we're going to use CN again so we already have it imported default class names are going to be h-5 w-5 and margin right 3 and dynamic class name is going to be route dot color so that's the color that we're going to use there we go you can see how now our little icon is displayed right here with its specific color and we load the route icon element we're just going to render route that label like this so there we go now it says dashboard what I want to do now is I want to give my link some specific class names so let's go ahead and write class name right here and we're gonna write text SM we're gonna write group blacks padding Dash 3 W Dash full justify Dash start font Dash medium cursor Dash pointer like that hover is going to be text Dash White hover is also going to be BG Dash white slash 10 meaning we are giving it an opacity rounded LG and transition at the end like that so now if you hover you see you have a nice effect for each of your route great so now what we can do is we can go back to our routes and actually create the rest of the routes so let's go ahead and do that I'm going to go ahead and copy this and for my second route I'm gonna name this conversation like this I'm going to change the icon to be message Square from Lucid react which I've imported at the top right here great and I'm gonna change the color to be Violet like this great now let's copy this again and for the third route let's write image generation like that and let's give this an icon of image icon from Lucid Dash react so these are my icons so far I have an image icon I have layout dashboard and message Square from Lucid react like that and let's go ahead and change the color of this to a pink 700 like that great let's copy this again and let's name this video generation and let's use the video icon again from Lucid react right here and let's change the color of this to Orange 700 like that I'm gonna copy it two more times so this one is going to be music generation and it's gonna have an icon called music so just go ahead and import that from Lucid react as well and change the color of this to Emerald 500 like that perfect and last one is going to be code generation sorry not last one we also have settings so this one is going to have an icon of code so you can import that from Lucid react as well like that and give it a color of green 700 like that and our last one is going to be simple it's just going to be settings icon is going to be settings as well so make sure you import that from Lucid react as well it's not going to have any color like this perfect so that looks much much better now and what I want to do now is just change each of their routes the dashboard is fine conversation is going to lead to slash conversation image generation is going to lead to slash image don't worry about this this is just hot reload adding you can refresh and it's going to be normal so don't worry about that uh for video is going to be slash video for music is going to be slash music uh for code is going to be slash code and settings is going to be slash settings like that and just refresh all of this so you don't have those things great and now if you try and click on something other than dashboard you're gonna get a 404 because none of those routes exist yet but they are gonna exist in the future uh perfect so now we actually have our working sidebar what I want to do now is I want to enable it on mobile as well because if you go ahead and collapse you only have the toggle button but no sidebar opening so let's see how we can do that let's go back inside of our navbar component so I'm gonna find it like this it's inside of your components folder right here and what you want to do now I'm still going to expand this just like this what you want to know is you want to replace this entire thing with its own component so I'm going to copy this button which holds the menu icon and I'm going to go ahead and create a new component called mobile Dash sidebar dot ESX like this let me just show you the name mobile Dash sidebar make sure it's use client and let's just quickly export that so mobile sidebar like that it's not going to have any props for now and you can go ahead and just paste this inside and make sure you add the imports from UI button like this but I'm going to replace it with components and menu from Lucid react like that and you know I like to separate my imports great now we have that mobile sidebar and now we can go back inside of nav bar and replace this with mobile sidebar like this and import it either from dot slash sidebar or I like to use components like this and you can remove the import from menu like that great and now make sure you are in mobile view because we're going to work on mobile now so either zoom in the page or collapse your screen so you don't see the sidebar and you see the toggle button great so we can continue working in the mobile sidebar now so I'm going to close everything besides that what we have to do now is we have to add a specific component from chat CN so go ahead and go inside chat CN Library so either use the Google or Link in the description go inside components and let's find the component we need which is a sheet so you can see how it works it opens like a drawer on the side which is exactly what we want and the way we install that is by using npx chat cn-ui at latest add sheet so I'm going to click copy I'm going to select npm I'm going into my terminal I will shut down the app and I'm going to press enter and I'm just going to confirm this installation by pressing y like this there we go so now it's installing sheet great and after a couple of seconds we can run the app again so if you shut down the app just make sure you refresh your application so it's up to date and collapse it of course on the mobile view perfect and now what you're going to do is you're going to add this sheet component from dot slash UI sheet like that and wrap the entire component inside of that but I'm gonna replace this from slash components like this it's still going to work and I'm going to replace this with sheet trigger sorry I'm not going to replace anything but I'm going to wrap it inside of sheet trigger like this and one thing I just want to tell you is don't accidentally import stuff from Radix because there are similar renamed components from Radix which are the underlying library that chat CN uses so whenever you import stuff from chat CN like sheet or button or sheet trigger make sure it's from add slash components UI sheet or dot UI like this so just make sure there's no Radix in your Imports you are not going to work with Radix directly great and just below that add sheet content also from UI components like this so I'm just going to collapse this so it's importing in that way and inside of my sheet content right here what I'm going to do is I'm going to give it a property side to be left and a class name of e Dash is zero so I want to remove any pre-made padding inside and I'm simply going to render this sidebar component from dot slash sidebar like this or from slash component sidebar like that and if you click now look at this absolutely amazing do you see how cool this looks exactly what we wanted and of course on desktop there is no toggle button perfect this is exactly what we wanted and one more thing let's see if we can improve this uh actually you know what it's fine for now let's leave it as it is we're going to change the colors later on you can see that our x button is actually dark but we're gonna solve that later for now this is what I want to do for this part of the tutorial uh great amazing one thing that we're gonna add later in the sidebar when we add subscription is that little feature on the bottom that is counting how many free Generations we have but we're gonna get to that once we import and stripe and all the other things great great job so far let's continue and let's actually create our dashboard page right here but before we do that I'm going to refresh a couple of times I want to bring your attention that I'm getting this hydration errors right here so let's quickly fix that they're coming from this const mobile sidebar right here so what I'm going to write is I'm going to write const is mounted set is mounted right here use state from react default value is going to be false then I'm going to write a use effect which I will import from react the same place I did with use State like this and I'm quickly going to return an arrow function I'm going to add an empty dependency array and I'm just going to write a set is mounted to true and in here I'm going to write if it's not mounted return now like this and now you can see that our hydration error has gone away so this is a simple trick you can fix components which are causing hydration Errors By the way they are rendered perfect so now that we have that and still before we go and do something on the dashboard page I quickly want to go back in my components inside sidebar.tsx right here and what I want to do here is I want to mark this as this client all right we already did that and then what I want to do is add a path name so cones path name is equal to use path name like this and we have to import that from next navigation so go ahead and import use path name from next slash navigation like this perfect and now that we have that I'm gonna go ahead and I'm going to click a command which is going to collapse all the code inside uh so it's not like spreading around so you can see everything in one file right here and I'm going to find my route.map link component and you have seen this huge class name that we have well we have to make it Dynamic that's because right now in my sidebar you have no idea what route am I on right but I am on slash dashboard so what I would expect is that dashboard is looking like this like when I'm hovering on it right but I want it to be like that regardless if I'm hovering or not I want it to show to my users that that is the currently active page so what I'm gonna do is I'm going to find the start of this class name the end of this class name and I'm going to wrap the entire thing in curly brackets like this and then I'm going to use this very same CN tool so CN I'm going to open parenthesis and I'm going to end them by the end of the annotations so now the entire class name is inside of the CN if I refresh everything is staying exactly the same then I'm going to add a comma to add a dynamic class name here and I'm going to expand this as much as I can like that and I'm going to write if path name so if current path name is equal to this route href that means obviously it is uh it is currently active and we have to highlight it in some way but I'm having an error here let's see I think I made a mistake yes so const path name like this great so now that we have the cons path name I can compare that I can add a question mark and I can add a dynamic class name which is going to be text Dash white and BG Dash white slash 10 else I'm going to use text sync Dash 400 like that so now you can see that my dashboard is highlighted by default because I am on slash dashboard if yours is not highlighted make sure that it's not misspelled right here in the routes constant beautiful so now that we have that let's go ahead and let's actually create our dashboard page so I'm just going to expand it a little bit I'm going to close everything I'm gonna go inside the app folder inside dashboard routes dashboard and page.dsx right here where we have uh this page right here let's go ahead and let's remove this text and let's create a div with the class name of text my apologies margin bottom Dash 8 space Dash Y dash four and inside let's create an H1 element uh maybe better H2 element instead and let's just write explore the power of AI like this and let's give it a class name of text-2 excel so that's going to be on small devices but on medium devices we can increase it even more so text-4 Excel like that let's give it a font bold and let's give it a text Center like this there we go below that I'm gonna add a paragraph which is going to say chat with the smartest AI X experience the power of AI or whatever text you want it doesn't really matter and let's give this a class name of text muted Dash foreground like this and you can see how now it's a bit muted and if you're wondering where did this class name came from that's because this is not a class name from Tailwind CSS this is a class name from chat CN UI so that's why it's working here now let's use Font light and let's give it the text of small on mobile devices but on medium we're gonna use text LG so a bit of a larger text and let's use text Dash centers it looks nice and in the center perfect now outside of this div right here open another div the class name of ex-4 on mobile devices on medium is going to be px20 on the large it's going to be px32 and space Dash Y dash four beautiful now let's go ahead and let's mark this page as use client because we're going to have some interactive elements inside what we have to do now is we have to create our tools array so we can iterate over them right here so let's write the cons tools is equal an array of objects it's going to have a label of conversation like this icon of message Square and you can import message Square from Lucid react like this let's give it a color of text violet-500 let's give it a BG color of text violet-500-10 my apology is not text but BG and let's give it an hrap of Slash conversation like that and now inside of here let's go ahead and let's iterate over our tools so tools.map tool and immediately go ahead and return a new element that we need which you can get from chat CN UI so we have to go to chat cnui and add a new component so let's go in here and in the sidebar I'm gonna go ahead and I'm going to find the card element right here so this is what we want I want this element right here so I'm going to use this command ntx chat cn-ui at latest add card I'm going to copy the command as npm and I'm gonna go inside of my terminal and I'm going to paste the command right here I'm going to confirm that I want to install it and I'm going to run npm run Dev like this so make sure you refresh your project after that beautiful so now we have a new component in our uh components style great so go ahead and add that so card from add slash components UI card make sure you've imported it like this and I'm going to separate my imports beautiful and let's go ahead and let's give it a key of tool dot href like this and I'm just going to collapse this element like this because I'm going to add a bunch of class names so it's a bit more readable for all of us let's go ahead and let's write last name to be b-4 border Dash Black slash Five Plex items Dash Center justify Dash between hover Shadow Dash MD transition and cursor Dash pointer like this beautiful so now you can already see a little box right here that has appeared because I have one element in tools and I am iterating over it and rendering this card with this class names and this class name has a class that when we hover we add a little Shadow to it right here so I can even zoom in so you can see a bit better it's very subtle but I think it looks nice great now inside of that card create a new div with the class name or Flex items Dash Center and GAP Dash x-4 like this and inside of that you're gonna create a new div with a class name open curly brackets use the CN library from add slash Libs urals like this and go ahead and open parenthesis let's write some default classes so that is going to be p-2 W Dash fit and rounded-md and a dynamic class name is going to be tool.bg color like that beautiful and inside we're going to render tool dot icon which is a self-closing tag and I'm gonna give you the class name again curly brackets opencn and parenthesis inside w-8 and h-8 for the default class and dynamic class is going to be two dot color like this there we go so you can see how nice that looks perfect and now just below this div which is rendering our our icon go ahead and create a new div with the class name of font semi bold like this and inside you're going to render tool dot label like that beautiful and now outside of this last div but still inside the card element you're going to render Arrow right component from Lucid react like this and it is a self-closing tag just make sure you've imported it here at the top and let's go ahead and give it a class name of w-5 and h-5 like this beautiful and now let's go ahead and let's actually add some more tools right here so I'm going to copy this same way we did with the sidebar so I'm going to write for the second one to be music generation the icon I'm going to use is music so import that from Lucid react the color is going to be Emerald like this and the href is going to be slash music like that let's go ahead and copy that again and don't forget to add a comma the third one is going to be image generation it's going to have an icon of image icon so import that from lucidreact as well I'm just going to collapse this so it's a bit nicer to look at there we go so we have the image icon now uh let's change the color to be pink and I'm not going to use 500 but 700 for both of these colors and href is going to be slash image beautiful let's copy that don't forget a comma this one is going to be video generation so video Generation image is going to be video icon so again make sure you have that imported from Lucid react the color right here is going to be orange 700 like that and let's go ahead and copy the last one which is going to be code generation the icon is going to be code so again make sure you have imported that from lucidreact and the color is going to be green like this and href is going to be code and we also have to change the href for video to slash video like that beautiful absolutely amazing you can see how nice this looks even on desktop and on mobile as well perfect and just one more thing I want to add before we wrap up the dashboard page is that when we click on this card so go ahead and find the tools.map find the card element and write on click to Be an Arrow function and right now we actually can't do anything we have to add the router so const router is equal to use router but not from next slash router we have to use next slash navigation like this so make sure you have imported use router from next slash navigation and I'm gonna move that to the top right here perfect and now that you have this constant use router let's go ahead and finish this Arrow function it's going to write router dot push tool dot href like this there we go so now when I click on conversation we are redirected to slash conversation but we get a 404 because that does not exist yet and that is true for all of those here beautiful you have finished the dashboard page for your application you have a working sidebar and a mobile cyber as well you also have implemented authentication great great job we can now finally go ahead and create our first AI tool which is going to be the conversation great great job so far now it's time to create our conversation page so what I'm going to do is I'm going to close everything I'm going to go inside of my app folder inside the dashboard route group inside routes and on the same level as dashboard I'm going to create a new folder called conversation like this and inside I'm going to create a new file page.dsx and let's go ahead and let's quickly export default a component called conversation page like this and all I'm going to do in the return is a div saying hello conversation like that and now that you have this conversation route right here you can go ahead and click on conversation either in the dashboard or in the sidebar or manually go to slash conversation and you are going to see that route right here so you can try it from here and from here it should be exactly the same because the href is matching perfect now what I want to do is I want to replace this text hello conversation with a heading component so let's go ahead and do that so I'm going to replace this with heading like this and now I'm going to get an error because the heading component does not exist so let's go ahead and let's create that inside of our components folder here create a new file called heading.psx like that and in here I'm going to export const heading like this which is going to just return a simple div saying heading component like that perfect and now that we have that we can go back inside of our conversation page and import heading from add slash components heading like this and once I save you can see that now it's rendering the text from that very same component now let's create an interface for this heading so interface heading props like this is going to have a title which is a type of a string it's going to have a description which is a type of string as well it's going to have an icon which is a type of lucid icon from lucidreact now when I did this in the first iteration of this project I actually didn't have this import I used icon but for some reason now it's a lucid icon so if you're just having any trouble with this you can just put any and save yourself any trouble if you're getting errors here but for now this seems to work perhaps a package has updated and changed that type or maybe I did something wrong in the initial project but regardless if this is working fine for you you can just continue like that if you're having any problems just replace this with any okay besides icon we are also going to have an optional icon color so put a question mark it's a type of string and an optional BG color like this which is also a type of string now let's go ahead and let's destructure these props so title description icon icon color and VG color like that and let's go ahead and assign the props heading props like this there we go beautiful and now let's go back inside of our conversation page and let's assign these props right here so we can better see what we are developing so the title is going to be conversation the description is going to be our most advanced conversation model or whatever you want really icon is going to be message Square from lucidreact and I'm going to separate my inverse like this great the icon color is going to be textlash violet 500 and VG color is going to be BG Dash violet Dash 500 slash 10 like this so nothing much to change now but you can see that there are no errors in this component meaning that we passed all the correct props if you're having an error with the icon that probably is referring to what I said before just replace this with any but it seems to work fine so you can just follow along now let's use these props to actually create something in this heading component let's go ahead and let's wrap this entire thing into a fragment first like this and now let's go ahead and let's give this div a class name of tx-4 on large you're going to have a bigger padding so px-8 Flex items Dash Center get Dash x-3 and margin bottom of 8 like that great now inside of this instead of that text create another div with a dynamic class name so open curly brackets and go ahead and import the CN library from our Lids like that so make sure you have that open parenthesis let's give it some default classes in my case it's going to be W10 and h10 and pass the dynamic icon uh oh my apologies I made the uh I wrote the wrong line so first thing we have to do is actually the container for the icon that was the class name for the icon my apologies so this is the correct class name p-2 W fit and rounded Dash MD and the dynamic color is BG color like this perfect and now inside of that go ahead and render the icon like this and we don't have this icon so we have to remap this icon to a capital icon so go into your props right here where the icon is and just remap it to Icon like this nice and now let's go ahead and let's give this a class name of CN like this w-10 h-10 and icon color like this there we go so now you can see how that very same icon from the dashboard is here as well that's exactly what we wanted beautiful now what I want to do is I want to go outside of this div right here I'm going to create a new div and I'm going to create an H2 element inside with a class name of text-3 Excel and font Dash bold like that and let's create a text rendering title like this but we made a mistake so I created this div outside of this main div and what I think we want to actually do is render it inside of that so not in uh the bottom of our fragment but right here like this there we go that's what I wanted to do my apologies so I will just indent this to look as it should and what I'm going to do inside of this div is just below the H2 element I'm going to create a new paragraph in which we're going to render the description like this and I'm going to add a class name here text SM tax Dash muted Dash foreground like this there we go so now we have a nice heading component that we can reuse throughout all of these models right here let me just expand this so you can see exactly how this looks and I think we actually don't need this fragment there we go yeah we don't need a fragment so just create a div which defines our padding on both small devices and large devices it is as Flex to Center the items and add some additional spacing between these items then we have the first div holding our icon and its background and the second div holding the title and the description and you can see how it looks nice on both mobile and desktop nice great I'm just going to expand this as much as I can so we can still develop here and now let's go back inside of our page.tsx because the heading component is finished and there's nothing more we can do here so just below this heading component we create a new div inside of your conversation page with last name of vx-4 but an lgbx-8 like this and what you're going to render inside of here is our form but we have no form elements yet so what we have to do is we have to go inside our chat CN UI right here and let's go ahead and find the form component right here react hook form like this so whoops I'm just going to expand it a little bit so you can see better and let's go ahead and let's find how to install that so we are using npx chat cn-ui at latest add form I'm gonna copy that as npm and I'm going to go inside of my terminal right here I will paste that and I'm going to confirm the installation like this and it's going to install a couple of packages so this is a bit of a larger component so we're gonna get react hook form we're going to get all the form elements and we're also going to get some validators so go ahead and refresh your application if you've shatted it down and and just make sure it loads normally and what we can do now is we can actually start building our form but before we do that let's go ahead and let's actually create some functions that the form is going to need so const form is equal use form from react to cook form which is a new package that we now have thanks to this command that we just run so they that installed this for us inside of this use form let's go ahead and let's define the default values my apologies default values are like this let's give it a prompt which is an empty string so we are only going to have one input which is going to take this prompt right here and now you see that I have an error because I'm using use form which is obviously in the hook meaning it uses a context so for that I have to mark this entire component as use client and once I save the error has gone away perfect what I want to do now is import Zod so there is a specific way we have to import Zod which we are going to use to create our schemas and a bunch of validation in our forms so import asterisks as Z from Zod like this beautiful and now what I want to do is I want to create a constants file where we are going to keep our form schema so let's go ahead and inside this conversation folder create a new file called constance.thes like that and go ahead and write again import asterisk as Z from zot and expert const forms schema to be Z dot object open an object inside prompt which is our one of our default values if you remember right here so let's go ahead and give it a type so it's going to be Z dot string it's going to be required at least one character and let's add a custom message if that is not true to be prompt is required like this beautiful so just make sure you export const this form schema and now we can go back in page DSX right here and let's go ahead and let's actually import that from our constants so I'm going to write import one schema from dot slash constants so no need to use the add sign here because we are only going to use it inside of this folder there we go so now that we have that we can actually Define the types for this use form so go ahead and just before the first parenthesis open this curly brackets sorry this pointy brackets and write z dot infer like this go ahead and open pointy brackets one more time type off form schema like this and then besides default values we can also add a resolver like this and for the resolver we are actually going to use Zod resolver like that and we're going to pass in form schema now let's go ahead and import the Zod resolver from another package that we got thanks to installing uh the form component from chat cnui so go ahead and write import Zod resolver from add hook form slash resolvers slash Zod like this so I just want to explain this that this at sign and this at sign is just a coincidence so this is the actual name of the package from npm and this is just our Alias to go to uh the root of our project so don't confuse the two you can see that there is a difference so this one is exactly next to the name and our has a slash so that's the difference this is the actual name of the package and this is our own component so I just want to explain that in case you're confused great now we have a Zone resolver which is going to control the validation for this form right here and now let's go ahead and just write two more things regarding the form that we are going to need before we start adding some elements here so I want to extract the loading State usually in my past tutorials I was using use state from react to control the loading state but thanks to some of your comments I figured out that use form actually has its own the loading state so I'm going to write a constant is loading here to be form.form state that is submitting like this and we're going to use that throughout this form and let's write a simple const on submit form it's going to be an asynchronous arrow function which takes the prop of values like this which is a type of Z dot infer type of form schema like that and it's just a simple arrow function and for now all I'm going to do is console log the values like that because we have no API ready to send it to beautiful so we have all of that now and now we are ready to actually build our form so let's go ahead and inside of this div create a new div and then you're going to add form component from add slash components slash UI form so just make sure you've imported this like that now that you have your form component here go ahead and pass in spread form like this so we're going to pass in all of the functions this form constant has which uses the react hook form so that is instead of us passing it all the props manually we can just do this and it's much quicker now inside of that go ahead and write the native HTML form element like this and inside go ahead and add an on submit prop which is going to be form dot handle submit go ahead and open parenthesis and then we're going to pass in this constant on submit that we've created above there we go and now let's go ahead and add some class names to this form so class name is going to be rounded LG border W Dash full p dash 4 PX 3 on medium devices is going to be X-6 so a bit of a larger spacing on larger devices Focus dash Within is going to be Shadow Dash SM grid and grid Dash course 12 and gap-2 and you should see a very small container visible inside like that nice now let's go ahead inside of this native Four Element and let's add a form field so form field like this and you can import that again from components UI form make sure you have it right here like that and form field is going to be a self-closing tag so let's go ahead and write a name for this field which is going to be prompt so it's going to control this value right here and we have the validation for that value right here great so this form field is going to control that value and now let's give it a render prop like this go ahead and open parenthesis go ahead and immediately the structure a field from that and just open an arrow function which is immediately going to render an element and that element is going to be our form item again from add slash components UI form like this make sure you've imported form item from add slash components UI form like that let's go ahead and give this a class name of call Dash span 12 so that's going to be for mobile devices but on large is going to be call Dash span-10 like that inside of that form item add a new component form component whoops form control again from add slash components UI form just check that it's imported right here so I'm just going to collapse all of this so you can clearly see and don't have to skew to see it great so we have the form control now and this form control is going to have a class name of margin 0 and padding zero like that and inside we have to add another component called input but our input does not come with our uh hook uh this hook at this ad form uh CLI so we have to add it manually by finding it here let's go ahead and let's find the input right here there we go and let's see how we can install it using npx chat CNY latest add input so I'm going to copy that as npm as well I'm going to go inside of my terminal and I'm going to run that Command right here I'm going to press yes and I'm going to run the project again and of course I'm going to refresh to make sure everything is running smoothly now that we have that let's go ahead and let's add the input from our add slash components UI input which is a self-closing tag so there we go so import input from add slash components UI input I'm just going to move it alongside all of my components right here beautiful so now that we have that let's go ahead and let's give this input some class names so I'm going to write a class name border Dash 0 outline Dash none Focus dash visible ring Dash zero Focus dash visible ring Dash transparent like this because I want it to be especially styled input I don't I want it to look like the whole container is an input so it's just a bit of my style preference if you want to you can completely remove these class names but then the input is clearly visible and I kind of don't like it so that's why I added these class names because I just think it looks cooler like it's a magic input inside of this box great and now that we have that input I'm gonna add a disabled prop to it and disable is going to be activated when we are loading the form so we're going to use this constant that we defined right here so just pass it inside like that nice and for the rest let's give it a placeholder which is going to be for uh you can write whatever you want here but I want to give it a prompt so the user knows what to ask this AI model for example how do e how how do I calculate the radius of a Circle for example so that's going to be one of the prompts that the user can write inside and let's go ahead and let's spread field inside which we've destructured right here in the form field so what is this spreading of the field doing well if you play around and go through these types right here you can see that that field has on change on blur value name and ref so what we've done essentially is replaced manually writing on change manually writing uh on blur manually writing value and all of that stuff by simply spreading field and we are trusting chat CN and react hook form that is going to handle it as it should beautiful so very very simple and now let's go ahead and just outside of this phone control outside of the form item and outside of the entire form field actually but still inside of the Native HTML form element add a new component button from add slash components eui button like this so make sure you've imported that here I'm going to move it alongside all of my imports and let's go ahead and just write generate like this but we have to give it some Styles so I'm gonna go ahead and write class name here call Dash span-12 on mobile devices and LG is going to be call Dash span-2 like that so right now it's not on large so it's collapsed but if I expand a bit you can see how it looks so depending on whether you are on a bit of a smaller device I decided to collapse it because it just looks uh better and so yeah we kind of shared the same design for our mobile and for our medium screens if you want you can change this LG and LG here to medium but I in my experience it looks better like this because there is a chance that some of our models like image generation are not only going to have an input and a button we're also going to have some select options so that's why it's better to just collapse everything like this immediately on the medium and the not on only in large great so now that we have that let's go ahead and let's also give it a w Dash full property just in case because we're going to need it later let's go ahead and let's give it a disabled prop of is loading as well beautiful so now we have that done uh and that pretty much finishes our form so what we have to do next is actually create the axios call well the axis coil that is going to generate something here but before we do that let's go ahead and let's actually prepare for what's next so outside of this form with the capital F and outside of this div right here go ahead and create a new div the class name space Dash y-4 and margin top 4 and for now all I'm going to write is messages content right so this is where we are going to render our messages once we actually have something to load beautiful great great job so far now it's time for us to add an API route which is going to connect to open Ai and actually make this site functional so in order to do that what you have to do is go ahead and create an account for open AI so either go to open ai.com or Google open AI find the menu and click on login or sign up depending on whether you have an account or not so I already have an account so I'm going to use my Google email right here and then I'm prompted with these three options go ahead and click on API like this and you should see an API platform just like this if this is your first ever time using open AI that means so if you had to register for this that means you have a free tier and you don't need to add your credit card to use this and you can check that very quickly by clicking on personal at the top click on manage account and click on usage right here so you can see that I don't have a free tier so I had to pay because during the development of this tutorial I intensively used open AI but you can see that even during my three weeks of using open Ai and rigorous testing I barely spend five dollars so it's not that expensive even if you end up having to add your credit card but if this is your first time creating this account you are probably going to see an entire bar grayed out and it's probably going to say 0 out of five dollars that means that you have five dollars for free that you can use without adding your credit card but you have to use it within the first three months if you wait longer than that it's going to be expired and then you're gonna see a red line for this usage right here so what happens if you during this tutorial you end up needing more credits well you go ahead and click on billing right here and add a payment method and after you do that go ahead and click on usage limits and make sure that you add a hard limit and a soft limit so this is not going to allow you to spend more money than what you enter here so right now I'm ready to spend ten dollars and I'm gonna click save and you can see in my usage I'm not able to spend more than ten dollars so they do have security measures in place for you to not overspend but again if this is your first time creating this account you most certainly have a free tier and you don't need to add a credit card so let's go ahead I'm going to zoom out a bit and now I want you to click on personal again and click on view API keys and in here I already have one secret key but which I used for initial project and go ahead and click on create new secret key and I'm going to name this tutorial video it doesn't matter what you name it but what does matter is that you copied this secret immediately and don't close the model just yet instead go into your environment file like this and add open AI underscore API underscore key and paste that key right here if you accidentally did close this model don't worry just click create new secret key again perfect so now that we have this that is actually it we no longer need open AI at all and we can go back inside of our application and now it's time for us to create an API route so go ahead go into the ape app folder right here and one more thing uh before we continue so what I want you to do is I want you to add your dot environment file to git ignore that's very important so go ahead in your git ignore file and just add under this dot environment.local just add dot environment as well and then your dot environment file is not going to be committed to GitHub perfect now let's go ahead and let's go inside of our app folder let's go and create a new folder called API and inside create a new folder called conversation like that and inside go ahead and create a new file called route.ds like that and now we have to install a package for open AI so go ahead in your terminal I'm going to close the app and write npm install open AI like this and after that I'm going to run the project again and refresh my page now let's go ahead and add the configuration for open AI so import configure and open AI API from open AI like this now write constant configuration foreign like that a new configuration and the objects are API key which is process.environment and it has to be this one in your open AI key like this and now just write const open AI to be new open AI API and pass in the configuration and now we can go ahead and write in the post route so export a synchronous function post like this which is going to accept a request which is a type of request and go ahead and open a try and catch block like this so let's resolve the error first I'm going to console log I'm going to add a string saying conversation underscore error and I'm going to log that error so this is going to help us in development and then just return new next response which you can import from next slash server and just go ahead and write internal error and just pass in the status of 500 like that now in try let's go ahead and check if we are authenticated so const user ID from out and you can import out from add clerk slash next JS like that perfect now let's go ahead and let's destructure the body so const body is equal to a weight request.json like that and cons messages are equal to body like this now let's check for authentication so if there is no user ID in that case we are going to return new next response unauthorized and passing the status of 401 like that after that go ahead and write if there is no configuration.api key return new next response open AI API key not configured which is the status of 500 like that now let's go ahead and check if we haven't passed the messages to this route so if there is no messages in that case return new next response messages are required like that with a status of 400 like that and now just write const response and now we're going to use the open AI model so we're going to write a weight open AI dot create chat completion we're going to choose the model the model we're going to use is GPT Dash 3.5-turbo like that and we're gonna pass in the messages model like that and all we have to do in the end is return nextresponse.json response dot data dot choices first in the array Dot message like that beautiful so you finished an entire connection to open Ai and you checked for authentication beautiful and now we are ready to go back inside the page of our conversation and actually make that request so let's go ahead and let's go back inside of our dashboard routes conversation page.vsx right here and first what I want to do is I want to install a package called axios so let's go ahead in our terminal and write npm install axios like this and run the project again and refresh it if you've shut it down and I'm just going to import that at the top so import axios from axis like that because of course we are going to use it so now what I'm going to do is I'm going to open a try and catch block on this and submit so try and catch like this error is a type of any like that and for now let's just log this error right here and then finally what I'm going to do is I'm going to refresh the router so all of our server components are going to update this is going to be useful in the future and I'm going to explain why when we get to that part so go ahead and add const router to be used router from next slash navigation so make sure is next slash navigation and not uh next slash router and I'm just going to add it to the top like this and just make sure you have the router right here and then in finally you can use router.refresh like that perfect now what we want to do in our try block is we want to do some modifications with the local messages and we want to create a post request to the new API router that we just created so let's go ahead first and let's create a state for our messages so I'm going to go ahead and just below the router right const messages set messages like this to be use state which you can import from react and go ahead and give it an empty array like this but I immediately want to give a type to this use state which is going to be chat completion request message and it's going to be an array and you can import this from open AI so go ahead and import from open AI like this so chat completion request message in the use State and make sure you put an array at the end of this type otherwise it's not going to work so this is how it looks in one line you state chat completion request message make sure it's an array otherwise it's not going to work and then on the empty array inside perfect so now that we have that we can go ahead and actually start assigning some messages here so what I'm going to write is const user message to be a type of chat completion request message again it's going to be an object which is going to have a role which is going to be user and content is going to be values.prompt so it's going to be whatever we have written in this input right here that's going to be the user message that's what we are sending and then we're going to add and create an array of new messages so cons new messages is going to be an array which is going to use all the existing messages which right now are empty but think of it that we are going to have many messages in this chat so we have to use all the existing message and add the new user message like that and now let's go ahead and let's create an API call for this so const response is equal to a weight axios which we have imported right here so axios dot post slash API slash conversation that is because in our app folder we have created a new folder called API conversation route TS so that results to slash API slash conversation and go ahead and pass in an object messages to be new messages like that perfect and now that we have our response you're going to use set messages it's going to take the current message value like this open an array spread the current add the user message and add response.data like that and make sure you do form dot reset at the end so the input is cleared perfect and I just want to do a comment inside of the sketch function so I'm going to add to do open pro model like this so this is going to help us later when we add subscriptions that if we get a specific error we're gonna have to know that we have to trigger a premium model there we go and what we can do now is we can actually modify this messages content right here to display our messages so let's go ahead inside and let's create a new div instead of this messages content so div last name Lex flat slash call Dash reverse the gap-y-4 and inside what we're going to do is we're going to iterate our messages so messages that map take the individual message and we're going to immediately return a div which is going to render message dot content like this and the key is going to be message dot well whatever we want let's give it content as well like this so let's see if this works right now it's going to be very simple right now but I'm going to open my network tab so I can see if everything is working correctly and if maybe if we are getting any errors for example what is the radius of the Sun and I'm going to click generate and you can see that we fired up an event on conversation and look at this we have an answer so this is our question and this is the answer the radius of the Sun is approximately well this many kilometers beautiful so our open AI model is officially working amazing amazing job look at this all that's left is to style this and to add some loading States and some empty States so let's go ahead and let's refresh and let's add the empty State first so what I'm going to do is I'm just going to expand this like this and I'm going to go inside of this div space y4 mt4 like this and just above this div which is rendering our messages what I'm going to write is a condition which is going to check if there are no messages in here so I'm going to write messages that length is equal to 0 and if we are not loading so if it's not loading and in that case go ahead and render a div which is going to say empty like this let's see if that works it works right here so the messages are empty and we are not loading meaning that it's going to display empty but instead of rendering empty what I actually want to render is a component called empty like this so go ahead and save this and of course we get an error because we don't have that component so let's go ahead let's go in our components and create a new file empty.vsx like this I'm going to go ahead and quickly create an empty component but I'm not gonna do export default I'm just going to do export const empty like this and inside I'm going to create a div saying empty component like that and then we can go back inside of this page and import that component from add slash components empty so just make sure you have imported it right here and you restructure the import because we didn't export const and if you save there we go now it says empty component perfect and I just want to move this react import all the way to the top like this there we go let's go back inside of our empty.tsx and let's add an interface for it so interface empty props label is string like that let's go ahead and instructure the label and let's give it the props empty props like that now let's remove this and let's give this a class name of H dash full e-20 Flex Flex stash call items Dash Center and justify Dash Center like that inside I'm going to go ahead and open another div with a class name of relative h-72 and w-72 as well and inside I'm going to render an image component from next slash image so make sure that you give it an out of empty a fill property and a source of Slash mt.png like that and if you refresh of course we have a broken image because this image does not exist in our project so obviously we have to add something to our public folder right here so if you want to you can find any uh image you like or you can go inside of my repository right here and go into the public folder and find the image called mt.png right click save the image and drag and drop that into the public folder and make sure it's named empty.png and now if I refresh right here there we go you can see that I have a nice looking empty State now one more thing we have to add to this empty component is to render the actual label so go outside of the div rendering the image and add a paragraph label like that and let's just go ahead and add a class name text Dash muted Dash foreground text SM and text Dash is center now nothing is rendering that is because in our app folder dashboard routes conversation page we did not add a label to this component so go ahead and add a label here and we're gonna say understand sorry no conversation started like this there we go now you can see how nicely this looks perfect now I want to create another component and that is for the loading state so I'm going to go ahead and create another conditional Above This messages saying is loading and end I'm going to open parenthesis I'm going to open a div with a class name b-8 rounded LG W Dash full Flex slash sorry Flex item slash Center justify Dash Center and DG Dash muted like this and inside I'm going to render the loader component and of course we're going to get an error if we refresh uh because loader does not exist oh I'm not getting an error probably because this is not the loading state right now but let's go ahead and let's create that component so components loader.tsx go ahead and write export const loader like this and return a div saying loading like that go back into page and import the loader from add slash components slash loader so the same way you did with empty like this and now you should have no errors at all let's go back inside of this loader component and let's add some class names to this div so class name is going to be H dash full Flex Flex Dash call gak Dash y-4 items Dash Center justify Dash Center like that and inside open a div with a class name w10h10 relative and animate Dash spin like that and inside we're going to use an image from next slash image give it an out of logo give it a property fill and give it a property source to be logo dot PNG like that and below this div which is wrapping our image add a paragraph saying genius is thinking and three dots and give it a class name of text SM text muted Dash foreground like this perfect and now let's go inside of our page.tsx and let's manually trigger the loading so I'm going to change this I'm just gonna change this is loading to true so you can see how this looks there we go you can see how our loader is now looking so just make sure that you have this exact code although if it's not looking like this for you beautiful and just bring this back there is loading like that nice and now what we have to do is we have to find a way to render these messages in a nicer way so let's go ahead and let's change this so I'm gonna add keep the key here like that but I'm just going to collapse it because we're going to have some more stuff here and inside go ahead and write class name it's going to be dynamic and we're going to use CN from atlibs utils so make sure you import CN from ad-lib utils like this open parenthesis inside and our first class the default class is going to be p-8 W Dash full Flex items Dash start Gap Dash x-8 and rounded Dash LG like that and then our Dynamic class name is going to be different depending on whether this is our message or the message from the AI so message that the role is identical to user in that case we're going to use BG Dash white border and Border Dash black slash 10 like this else we're going to use BG Dash muted like that there we go and now what I want to do is I want to create two more components user Avatar and what Avatar components so let's go ahead and let's go inside of our components again and create a new file called user Dash avatar.vsx I'm gonna go ahead and write expert cons user Avatar like this and I'm gonna extract the user from use user using clerk like this and I'm going to return and now we have to add the Avatar component from chat CN so let's go to chat CN right here let's go inside of components and let's find the Avatar component right here so this is the one we want let's go ahead and copy this I'm gonna go inside of my terminal and this is the command we're going to use npx chat cn-ui at latest add Avatar like that go ahead and confirm the installation and once it's done run the project again make sure you refresh your application and now you can freely add avatar from dot slash UI Avatar or from slash components out there so I like to use it in this way there we go go ahead and give this a class name of h-8 and w-8 go ahead and add another image also imported from s slash components or dot slash UI Avatar like this and the source is going to be user question mark dot profile image URL like that below that use the Avatar fallback so if an image well isn't able to load so I'm just going to structure this like this so you can see all the components we need we're going to use the Avatar feed Avatar fallback in case the image wasn't able to load so I'm going to write user question mark first name question mark Char at zero and I'm going to repeat that for the last name like this so in case my image can't load I'm just going to use my first first letter of my first name and first letter of my last name because that's what most commonly that's the most common way of fallback perfect and now that we have that let's also create a bot Avatar so go ahead and create a new component called bot-avatar dot DSX export const bot Avatar like this it's going to be much simpler so just return avatar from UI Avatar or components other like I prefer it like this let's go ahead and give this a class name of h-8 and W Dash 8 and Avatar image from components UI Avatar like that give it a class name of p-1 and the source of Slash logo dot PNG so we already have that image because it's in our sidebar and in our loading component perfect so we have those two now and we can now focus on building uh the messages design here so what I want to do besides rendering the message content and this div which we created right here is decide whether to render the user Avatar or bot Avatar so besides along above the message content I'm going to write message.roll is user in that case we're going to render user avatar from add slash components user Avatar so make sure you've imported that and otherwise I'm gonna render the bot Avatar like this perfect so I'm going to expand this you can see it in one line so make sure you have user Avatar and what Avatar imported like this let's go to the bottom and one more thing we have to do is I want to wrap this message content inside a paragraph like this and give this paragraph a class name of text SM like that and let's see how our messages look now so I'm gonna go ahead and say what is the radius of the Earth for example and there we go genius is thinking and you can see that he could you saw how for a second it used the fallback and then it uh loaded my image and here we have the answer absolutely amazing you have your first ever working AI model you can ask it whatever you want for example what are the best Technologies to build a web app in 2020 for example and genius is now thinking and it's gonna give us an answer in a couple of seconds let's see what is going to come up with there we go look at this say tells us exactly what are the newest libraries and everything great great job you successfully finished the conversation model is available on mobile it's available on desktop it's fully styled and you're using open AI beautiful amazing amazing job the next AI model I want to create is code generation because it's very similar to this conversation that we have right now and it also uses open AI so let's go ahead and I'm going to close everything I'm going to go into the app folder dashboard routes and I'm going to copy the conversation and I'm going to paste it inside routes so now you're going to have conversation and conversation copy like this let me just expand this there we go and rename the conversation copy to code like that so now if you click on code generation or go to slash code you're going to see the exact same thing as conversation so let's slowly go ahead and change it so that it fits our design for code first I want to change the cons conversation page and the export default to be code page instead of conversation page so I'm going to select the two and write code page like that perfect so nothing should change right now but we are writing proper code inside most of the stuff can actually stay this same in here but one thing we're going to change of course is the API and the design so let's start with the design first because I think it's much simpler to do that okay so inside of this heading let's change this to code generation like this make sure you are on slash code like this make sure you're not accidentally editing the conversation so this should stay the same but code generation should now say code generation on API sorry on slash code like this and the description is going to be generate code using descriptive text like that and the icon is going to be code from Lucid react so now let's go ahead and let's just remove the message Square import because we don't need that now uh great so we have that now and now let's change the colors so instead of violet it's going to be green and instead of 500 it's going to be 700 like that perfect so now it matches what we have on our dashboard right here and I can also click here and it should lead me to the exact same route if it's not make sure to check the tools constant inside dashboard and make sure that the href is correct it should be slash code the same as in the sidebar beautiful so we now have that let's go ahead and let's change the placeholder of the input instead of asking us how do I calculate the radius of a circle let's give this a more descriptive prompt to what the user could actually type in here we're going to go ahead and write right simple toggle button using react hooks like this so that's something more realistic to what the user would write in code generation AI model beautiful so now that we have that what I want to do is I want to edit the API so instead of going to slash API slash conversation in the on submit function inside of course the code page we're going to change it to slash code like this this of course right now does not exist so let's go ahead and let's create that we're going to go inside the app folder inside API we're going to copy the conversation paste it rename the new one to code like this and inside route.ds let's go ahead and let's see what we have to change uh to make our code generation works so what I want to do first is I want to add a default message to this model so it knows how to behave so I don't want it to be a general AI assistant right now it's just an assistant right because it has no messages in the beginning so we are going to add a default message to tell it how to behave in this model so let's go ahead and write const instruction message which is a type of chat completing chat completion request message and let's import that from open AI like this it's going to be an object which has a role of system like that and content is going to be well you can play around with this you can test what works best but I'm using this prompt because it works for me this does this is not set in stone you're going to see what I'm talking about so I'm gonna tell it what to do so you are a code generator like that you must answer only in markdown code Snippets use code comments for explanations like that so that is going to be the first message that this model is going to have so now let's copy this instruction message let's go inside the response right here and let's modify these messages to always take the instruction message first and then the rest of the messages like that everything else can stay the same except this error I'm gonna write code error so it's easier for us to find that in the terminal if something goes wrong so let's go ahead and let's test this so I'm going to refresh this and I'm gonna ask it one question first what is your purpose for example I think it should it should be probably it's probably going to be different for you so we're working with AI here but there we go you should say something in this line my purpose as a code generator so that's why uh the reason it's saying this is because we gave you an instruction message first so it knows that it is a code generator of course yours is probably not going to give the exact same answer but it should be something to indicate that it should generate code whereas if you go to conversation of course and ask what is your purpose I don't think it should mention code generation at all see it's an AI developed by openai and its purpose is to assist and provide helpful information but code generation is obviously he obviously knows that it is a code generator beautiful so one thing I want to test now is simple toggle button using react hooks let's see what it's going to come up with it is definitely going to give us some code but it's not going to be formatted in the way we want to what I want to do is I want to have a nice markdown see this is very hard to read like almost impossible so let's go ahead and let's go inside the page of code generation so app dashboard routes code page.psx right here so as I said everything else can stay exactly the same but one thing that I want to change is that I don't want to just render the message content like this instead I want to render it in react markdown so let's go ahead in our terminal and let's install a package called npm install react Dash markdown like that and after that has been installed you can go ahead and npm run Dev your project again and make sure to refresh perfect now let's import that so I'm going to import react markdown from react Dash markdown like that perfect and now I'm gonna go and find my message dot content and I'm going to modify it so let's go ahead and let's remove this for now and instead I'm going to write react markdown like this and inside just go ahead and render message.content pipe pipe empty array like this and let's see if that has improved anything so simple toggle button using react hooks let's see if that has changed how our code looks now there we go okay so we are getting somewhere but obviously it's still not perfect but it's at least structured to some way so let's see how we can improve it I'm gonna go back to react markdown here and let's see if we can give it some class names some components anything to make it look a bit better and I suggest that you don't refresh this so you can see the active changes while you're developing let's go ahead and give it a prop components and inside open another object first component I want to edit is the pre-tag because that's what it's using to render out code so we're going to edit that element to the way we like it so go ahead and open an arrow function and immediately destruct your node and the rest of the props like this go ahead and open an arrow function and render a div inside so div like this give it a class name of overflow Dash Auto W full margin top and bottom to two BG Dash Black slash 10 p-2 and rounded Dash LG and inside render the pre-tag and spread the props and it is a self-closing tag like this there we go now it looks a little bit better let's see if we can improve it even more I'm gonna go ahead and edit another uh element inside called code and I'm going to do the same thing I'm going to destruct your node and I'm going to spread the props and I'm going to return the code element which is a self-closing tag I'm going to give it the class name of bg-black Slash 10 rounded LG and padding of one and don't forget you have to spread the props inside like this so I'm just going to expand this so you can see there we go so you can see how now even our little codes Snippets have a specific color beautiful I'm just going to collapse this so you can see better and now what I want to do is I want to add so outside of this components prop to the react markdown component add a new Prop class name and in here let's give it a text SM overflow Dash hidden and leading dash 7 like this there we go now it looks much much better perfect look at this absolutely amazing let's let's ask it something else for example a model using react Hooks and Tailwind like this let's see what it's going to come up with and how it's going to look like there we go look at this I think this is absolutely amazing you can see how we have an overflow in our code snippet but we can scroll that without affecting the rest of the website I think this is really really cool and looks really really nice the AI knows its purpose and it can generate nice looking code in nice markdown beautiful job you just finished code generation as well as conversation so now we're gonna get uh and do a image generation because that is the last uh model which is going to use open AI the rest of our models are going to use replicate AI so that's what I wanted to go in this direction because it's easier for us to code so next one is going to be image generation that's going to be a bit different let's create our image generation AI page so what I want to do is I'm going to expand this I'm going to close everything and the same thing I did for code generation so I'm going to go and find the conversation I'm going to copy it and paste it inside routes and I'm going to rename this one to image like that and now if you go and click on image generation you should see conversation but not a404 and same thing if you go from the dashboard like this so now let's go ahead and slowly modify this so it looks like an actual image generator so first let's rename the conversation page constant and the export to image page like this and nothing should change yet we are not going to work with messages and we're gonna have to change the default values but we're going to do that later first I just want to change the design so it looks like an image generator so the title in our heading should be image generation like that beautiful description should be turn your prompt into an image like that the icon should be image icon from Lucid react so make sure you import that and remove the message Square because we no longer need that the colors should be pink like this and it should be of 700 value like that so it fits exactly what we used in the sidebar beautiful so you can see how it looks exactly the same here and now what I want to do is I want to change the constant for my form schema before I start doing anything else here so let's go ahead and let's go inside of our constants so inside of this folder which we copied we also copy the constants file so inside what I want to do is I want to change the form schema to change the message prompt instead of prompt is required let's change this to image prompt is required and let's also add an amount which is going to be Z dot string with a minimum value of 1 and resolution which is also Z dot string and a minimum value of 1 like that and now let's create constants for options so this image generation is going to have two options available you'll be able to select the number of images you want to generate as well as the resolution so we have to create that in our uh we we just did that in our phone schema but now I want to create an array of options that users will be able to choose from so let's go ahead and let's write export const amount options like this it's going to be an array of objects value for the first one is going to be 1 and label is going to be one photo like that let's go ahead and let's copy that and don't forget to add a comma and the second one is going to be 2 and of course photos like that and we're going to copy that a couple of more times until we reach five so let me just remove the extra comma let's change this to five like that there are of course smarter ways to do this but sometimes simple is uh better so I just change this to five I don't know why I meant on three and here I meant on four and the last one is going to be five like that so just modify this to five photos and the same way we did this I will want to do our resolution options so let's go ahead and write expert const resolution options like that first object inside is going to have a value of 256 times 256 and it's going to have the very same label go ahead and copy this and the second one is going to have a value not of 256 but of 512. and the last one is going to have 2020 or so 2024 like this there we go so we just created in the options that users will be able to choose from when creating their images and now we can go back inside of our image page right here and let's slowly start modifying things so first thing I want to remove is this images is the messages we're not going to have that here and we can also actually remove the chat completion request message from openai we are not going to need that great I'm going to modify this on submit right here so I'm going to replace this I'm just going to leave the response right here and for now let's go ahead and just do this for the set messages I'm also going to remove it like this great so just make sure that your own submit function is now much simpler and of course we're going to change this to slash image but let's limit it to slash conversation for now so we don't change too many things at once and get confused so our on submit is now calling the response and resetting the form that's it now we have some errors at the bottom and that is of course because of uh it's looking for the message state but we just removed it so it doesn't exist we're going to fix that very simply so we're not going to remove this but what I do want to remove is this I'm going to remove this entire div iterating over our messages so select this remove it create a new div and just write images will be rendered here like that and of course we still have the error because messages is not defined instead of messages we're going to use images so let's go ahead and let's create a constant images set images like this new state and an empty array inside and the type of this state is going to be an array of strings like this so now we have the images to work with so now let's go back here and let's change this to be images.length like that and let's immediately change this to no images generated like that there we go I also want to modify the loader just a little bit so I'm going to remove this huge class name we have for the container holding the loader and I'm going to replace it with just p-20 like that so let's check how that looks like there we go that looks fine now let's go ahead and let's change our form schema right here sorry not the form schema but are the default values in our use form hook right here so instead of just having a prompt it's also going to have amount and this amount is going to be by default just one image and resolution well you can choose what you want but I'm going to choose 512 like this beautiful and now that we have that we can go ahead and actually modify our on submit function here so what I want to do first is I want to reset all the photos every time we click submit so const set images and the array like that uh actually not constant just set images my apologies great and now what we're going to do is we're going to get this response but not from slash API conversation instead we're gonna get it from slash API slash image and the body we are getting the best inside is going to be values so we don't have to modify values in any way it's find the way it's passed from the on submit prop right here in our conversation page we had to modify the values to create a specific message object which fits the openai schema but this is going to be just fine because we're going to modify it on the server because the structure is simply that way that we can do that great and we're going to prepare the way our response is going to work so I'm going to write const urls response.data.map you're going to get the individual image which is going to be a type of object which has URL inside and that is a type of string and all I'm going to do is I'm going to extract image.url from that specific image we are iterating over so we are creating an array going through this respond those data and we're just going to return the URL string that is because our set images State expects an array of strings so we extract the URL from each individual image in the array that we are going to get from the axios and we set it in that state actually we don't yet set it we just created the URLs so just uh after the form reset before form reset let's do a set images URLs like that great so now that we have that we can actually go ahead and create our image route my apologies we cannot do that yet what we actually have to do is we have to add the options to select our Fields right here so let's go ahead and do that go ahead and find your form and let's first change the placeholder here so instead of asking how to calculate the radius of a circle let's ask a picture of a horse in Swiss Alps like this so that's a more realistic prompt that the user would ask great and now let's go ahead and let's create another form field that is going to be besides this input so form field again which is a self-closing tab let's go ahead and give it a name of amount and let's give it a property control to be form dot control so this one is going to be a little bit different because this time it's going to be a select input so that's why I'm adding control separately now let's add the render field go ahead and extract yield in this Arrow function and return form item component which we already have let's go ahead and give it a class name of call Dash span-12 and on LG it's going to be call Dash span Dash 2 like that great inside of that let's go ahead and add the select component the problem is we don't have it so go in chat c and UI of course and find the select component and it's right here there we go and now let's find the CLI command so npx chat CN Dash UI latest ad select I'm going to copy the npm I'm gonna go inside of my terminal like this and I'm gonna pass the command so npx chat cnui at latest add select and I'm going to confirm the installation and after that I'm going to go ahead and run my project again so just write npm run Dev again and refresh your application there we go and now we can safely add the select component inside so I'm going to add select like this from add slash components UI select so let's see how that looks like so I've added it right here select from s slash components UI select so let's move this well you can keep it here but just make sure it's alongside this Imports and yeah you can remove the user Avatar and the bot Avatar we don't need that and I'm pretty sure we are going to need CN I'm going to leave it just in case great now let's go ahead and let's continue developing this select component right here so let's give it some attributes he's going to have a prop disabled to be triggered when we are loading like that on value change is going to be field dot on change value is going to be field value and default value is going to be field dot value as well like that now let's add a deform control form control like this he's not going to have any attributes at all now let's add the select trigger component from add slash components UI select so make sure you have imported select trigger and select from components UI select inside of Select trigger go ahead and add select the value another component you're going to import from add slash components UI select so make sure you have that as well great and this is actually going to be a self-closing tag like this and just give it a default value of field dot value like that outside of our form control let's go ahead and let's add select content from s slash components UI select and inside let's iterate over our amount options so amount options from constants so I've imported select content from components UI select as well let me just structure this like this so you can see and I have imported amount options from the same place I'm importing form schema dot slash constants so make sure you import that as well and then go ahead and map over those like this pick the individual option like that and immediately return select item another item you are going to import from components UI select beautiful now let's go ahead and let's give it a key value up option dot value and let's give it a value of option.value as well and inside we're going to render option.label like that there we go you can see how now we have a prompt to select how many photos we want to generate beautiful and let's see how that looks like on desktop I think we made a mistake here and yes so we have to change the grid for our button and for our input so let's go ahead and play a little bit with that to see what we have to change exactly so let's find the prompt first so this is our prompt form field with the name prompt currently on large devices it uses call span 10 and that is way too much because we're gonna have two selects beside it and we want to be and want them all to be in the same line so we have to reduce this from 10 to 6 on large devices so I think just even now if I Collapse there we go looks much better but still something is a little bit weird but let's continue developing and let's add another select option here this time it's going to be for resolution options so you can go ahead and copy this entire form field before name amount so all the way to the end of the self-closing tag go ahead and paste it like this and save it and just rename this to resolution like that and let's go ahead and change these amount options to resolution options and make sure you have imported resolution options from uh dot slash constants like this so I'm gonna collapse all of those and save and I'm going to refresh here and there we go now it looks much much better on mobile and on desktop you can select your resolution and you can select the amount of photos you want beautiful let's see if we have to change anything in the button or if it looks fine so let's see our button is taking an LG call span of two and I think that is fine yes that can stay exactly the same and let's just test if this works so I'm going to comment this out for now and I'm going to comment actually everything besides set images you don't have to do this but I just want to console log my values to see what we are actually getting so we're going to expand this right here I'm going to open my tab I'm going to select I'm going to write a horse in Swiss Alps I'm gonna select two photos and 512 and there we go we have a prompt we have an amount and resolution everything seems to be correct let's change it there we go so this is what we will be sending to the server and openai knows how to work with that beautiful so I'm going to go ahead and I'm going to undo these changes right here and now it's time for us to create an API for our image generator let's go inside of our app folder again find the API folder and let's just copy the conversation and rename it to image like this go inside that route right here and let's see what we have to modify so user ID is fine body is fine but what we are destructuring from the body is not correct instead of messages we are going to need a prompt we're going to give an amount and let's give it a default value of one we're also going to need a resolution and let's give that a default value of 512 times 512 like that now instead of checking if we have the messages let's check if we have a prompt like this and say prompt is required like that and let's copy this two more times one for amount and say amount is required and one for resolution and say resolution is required and now instead of using chat create chat completion what we are going to do is we're going to use openai dot create image like this this one does not accept model or messages instead it exempts a prompt you can write it either like this or use a shorthand like this a number which we have to parse so parse integer amount with a Radix of 10 like this and size is a resolution like this there we go and the response is now different so we have to modify it so instead of using response.data.choices0.message go ahead and return the entire data so response dot data dot data and change the conversation error to image error like this beautiful now let's go back inside page.tsx and let's see if we can find a way to Simply render our images already so I'm going to go ahead and let's give this div a class name of grid grid again Dash calls dash one that's going to be on mobile on medium we're going to use grid Dash called stash 2 on large we're going to use grid Dash calls Dash three on extra large you are going to use grid Dash calls-4 and give it a gap 4 and margin top 8 like that now let's go ahead and let's write images.net source and immediately return a card which we already added in chat CN so make sure you import card from components UI card like this head and give it some properties so it's going to have a key which is source and it's going to have a class name which is going to be rounded LG and overflow Dash hidden inside card create a div with a class name of relative and aspect Dash Square inside of that create an image from next slash image so make sure you have imported image from next slash image like I did right here and I'm just going to move it to the top here so image from next slash image let's go all the way down back to where we added an image it is a self-closing tag let's go ahead and give it an ALT property of image a Field property and source which is going to be Source like this beautiful and inside the card let's also add card footer something you will also going to import from add slash components UI card inside of this card folder go ahead and give it a class name of p-2 and inside add a button which we already have imported so just make sure you have it imported but you should have it because we already use a button in this page like this and give this button a variant of secondary and the class name of w Dash full like this and inside I'm going to render download icon from lucidreact so make sure you have imported download and image icon from lucidreact like this let's go ahead and let's give it a class name of h-4 W-4 and margin right 2 like this and let's write a text to download like this and I'm going to collapse these attributes in this button because I want to add another one like this and let's add on click to Be an Arrow function window dot open source like that let's see if this is enough for us to render our images I'm gonna go and expand this I'm going to refresh the entire page I'm going to write a picture of a horse in Swiss Alps I'm gonna say to select four photos in the lowest resolution and let's see if that's gonna throw some errors or if we did everything correctly after a couple of seconds I'm pretty confident yes it actually works but what's going on why are we having this error well what's going on is that you cannot just load any image you want from uh in xjs using the next image what we have to do is we have to add this host name so this exact host name inside of our configuration so go ahead and copy this hostname right here so you're gonna get this error in the lead Source prop for this specific image on the next slash image which is the component we're using and it says exactly what is the hostname that we have to add inside of our next config and it also gives you instructions on how to do that so let's go ahead and let's copy this hostname it's a very long host name this and let's find our config so I'm going to close everything and find next.config.js like this open this object and write images domains an array and paste it inside like this and what I recommend actually doing is shutting down and restarting your application so NDM run that again I'm going to zoom out a bit I'm going to refresh and once this page loads we're gonna try our prompt again let's try one more time a picture of a horse in Swiss Alps for photos lowest resolution genius is thinking and let's see if this is going to be a success or not in a couple of seconds and there we go look at this absolutely amazing we have four images of a horse in Swiss Ops and we can also open this image individually and download it beautiful you officially know how to create your own chat GPD your own code generator and your own image generator you're doing an amazing job so far we only have a couple of models left to do so video generation and music generation is going to be done using replicate Ai and after we finish that we're going to implement stripe beautiful let's continue and let's create a music generation API so we're going to go ahead and we're going to do what we already did a few times and that is we're going to copy the conversation right here in routes and paste it and we're going to rename it to music like this now if you click on music you should see the conversation the same thing as we did a couple of times and same thing if you go from the dashboard so now let's go ahead inside Music Inside page.tsx right here and let's slowly modify this so it fits exactly what we want our music page to look like so first things first let's modify our heading instead of conversation it's going to be music generation like this in the description is going to be turn your prompt into music like that and we're going to change the icon to be music from Lucid react so make sure you import that and remove the message Square because we do not need that number eight and the colors are not going to be Violet they're going to be Emerald like this and 500 is correct now let's just quickly change the placeholder in our prompt form field so instead of this question I'm going to write piano solo like this great and what I want to already do is I just want to replace this entire div rendering the messages so go ahead and replace this entire div create a new one and just write music will be generated here like that and let's change this empty label to no music generated like that and now what I quickly want to change here so I'm going to expand this so you can see more of my code I want to change this messages State it's not going to use this type so you can remove this already and let's change the constant messages and set messages to be music and set music like this it's not going to be an array instead it's going to be just a type of string like this while we are here let's also remove user Avatar both Avatar and CN from our Imports because we're not going to use them and let's rename the page from conversation page to music page so make sure to rename the export default and the constant right here and now let's change our on submit so it matches our uh set music state so we so we're not going to need this at all we can remove that let's just go ahead and write set music on the phone every time uh we click submit the response is going to be a bit different so you can change it like this you can remove this set messages for now and change this to instead of Slash API slash conversation to slash API slash music music like that and just pass in the values and then you're gonna write set music response.data.audio like this and reset the form great and let's just see all the other errors we have so in here we are using we are checking for messages.length so let's go ahead and change that instead of checking messages length we're just going to change if there is no music like this there we go and now we are ready to actually create the API route that's because the constants the form schema file can stay the same so prompt is required if you want to you can change this to music is required but we are actually not even showing this prompts great so our form schema is correct and it can stay the same and what we have to do now is we have to go ahead and copy the API for conversation paste it and rename it to music like this route the TS here and a lot of stuff is going to be changed here actually because we are no longer using open AI for this we're going to be using replicate for this so let's go ahead and go to replicate.com or just Google replicate ai go ahead and find the sign in button or get started and create an account so you can go inside of your dashboard and this is how my dashboard looks like because I'm already using replicate and you can see I've run a lot of models here already but replicate as well as openai have a free tier if this is your first time using it open AI has a bit more explicit way of telling you how much of a free theater you have but I'm not sure about replicate but just be careful and don't spam too much and yes you don't need to add your credit card you can start using it for free so in order to find your API key what you have to do is click on your username right here and click on API tokens and then you can go ahead and copy this and let's add it to our environment variable so go back inside your code I'm going to close everything go inside dot environment right here and let's go ahead and let's add replicate underscore API underscore token like this and just paste that in here beautiful and now that we have this uh I just want to show you how to add a limit to here as well so if you end up needing more credits and add your credit card you can go into billing right here and you can also add a spend the limit so if you're ready to only spend ten dollars change this to 10 and click update monthly spend limit and then you will not be charged more than that so you can see that in my rigorous testing over the past few weeks of building this application I barely used up seven dollars so seven dollars in replicate and seven dollars on open AI so 14 in total in heavy testing of this project so I'm pretty sure that you're going to be able to build this entire thing on a free tier but just in case you can see it's not really that expensive great so now that we have that let's go back inside of our application and let's go back inside of our app folder API music route.ts right here and what we have to do is we have to install a package for replicate so go inside of your terminal right here and write npm install replicate like this wait a couple of seconds and run your application again and restart I mean refresh your web application great so you can remove everything regarding open AI we are not going to need it instead import replicate from replicate like this you can go ahead and remove this configuration and open AI constant and instead write const replicate to be new replicate like this alph is going to be process.environment and you're going to use the key from your environment variable so copy it from here and paste it here like this you're going to have an error here because there is a chance that it is undefined so you can fix that by either adding pipe pipe empty string or you can put an exclamation point at the end like this great now let's go ahead and let's modify this API Handler so it generates music so what we have to change in here is modify the message from the destructuring of the body to prompts like this then we can remove this check for configuration API key that is for openai like this and instead of checking if there are messages we're going to check if there is a prompt and write prompt is required like this and now our response is going to be a little bit different so we're not going to use open AI we're going to use replicate but in order to find the correct model it's really hard to write because it's a very long model so let's go ahead and let's go into replicate and let me show you how to use this so going to replicate and find the explore button right here and here you can see a bunch of models that they have so feel free to play around with any of these as you want you see they have a music generation bark a bunch of stuff right so I'm going to show you which one I am using to create music and then later once you finish the tutorial you can play around with any of those but just be careful if you are on a free tier because you're going to use them up so go ahead and find the audio generation models in the collections right here and I'm going to use refusion right here and here you can see and you can test out try and generate one of your own change the test here but don't change too much don't play around too much because I don't want you to lose your free tier and basically once you click on the API here you can see how you have to uh how you can actually use it so we already did this and what we have to do now is we have to copy this so this is the you can see the exact code that we need to call to actually generate music so I'm going to copy this output which is a weight replicate.run and you can see how it uses this very long text I'm actually not sure if we need this this looks like some kind of hash um but I I haven't tested it without it so let's go just go ahead and copy this right here and let's go ahead and replace this response with that like this so I'm going to expand it as much as I can so you can see and I'm going to change the output to response like that and we're going to change this prompt a which is hard coded to Funky synth solo to be our prompt which we're receiving from the body like this and then let's go ahead and just pass in the entire response back and let's change this to music error like that and now let's go back inside of our page so inside the dashboard routes music page.thesx right here let's go all the way down where we write music will be generated here and let's change this to be an actual audio file so I'm gonna go ahead and I'm gonna remove this div I'm going to write a conditional so only if there is music in our state in that case we're going to render an audio element from native HTML we're going to enable the controls we're going to give you the class name of w Dash full and margin top 8 like this and inside we're gonna give a source which is a self-closing tag with a source of music like this so let's go ahead Let's test if this is working I'm going to write piano solo like this and I'm going to click generate and in a couple of seconds we're gonna see if this is working or not but don't be scared if this is taking a long long time so the way I replicate AI works is it has a cold start so if this is your first time running it you might wait up to a minute or more for this to be generated but next instances are going to be much faster because it's not doing a cold start so just be patient and the way you can check if everything is correct you can go ahead in your dashboard right here and you can see that I have refusions starting 28 seconds ago so if you see that here that means everything is good your API is working and just be patient and wait till it says succeeded right here so I'm gonna pause the video and I'm gonna enable it once it's finished there we go you can go ahead and play it I'm not gonna play just in case someone decides to copyright AI music in the future but I want to show you how long it took me to do this so let's see I'm going to refresh here it took me 28 seconds to actually run it but the cold start was almost 12 minutes so don't be scared if yours is still running it's completely normal but the second one you try is going to be much shorter than that and if you actually take a look let's take a look at the I don't know stable diffusion for example every API actually has a web Hook when it's completed so if you are a bit more advanced and know how to work with web hooks you can go ahead and add a web hook to your API that way you don't have to hold your uh API routes into loading for such a long time almost 12 minutes you can use a web hook to trigger the event as completed and then you can show a notification for your user that's a bit more advanced and I'm gonna save that for some other tutorial but there are options you can speed up this if you want to great so now you know how to generate music you can change the volume you can play it you can download it amazing you're doing so great and it also is responsive on mobile as everything so far let's check it if we go from the dashboard and click on music it works as well amazing so all that's left is video generation and that's what we are going to do next let's create our last AI model video generation so we're gonna do the same thing with it a couple of times but this time we're going to copy the music app so let's go into routes inside of your dashboard right here go ahead and copy the music folder and paste it inside of the routes and rename a music copy to video like that the reason we are copying music is because it's going to be more similar than other tools to the one we want right now which is video and now if I try and click on the video I get music generation instead of a 404 and let's just quickly check if it's the same from the dashboard it is great and now what we have to do is we have to obviously modify this a video page to look like video page and not music generation so make sure you are on localhost 3000 slash video make sure you're not accidentally editing the music folder go inside video folder page.tsx and let's already remove this import we don't need so chat completion request message from open AI we don't need that here great now let's go ahead and let's go and change this music generation title into video generation and let's change the prompt uh the description to turn your prompt into music into video like this the icon is going to be file audio sorry the icon is going to be video icon like this from Lucid react so make sure you import that and remove music like this and now let's go ahead and change the colors so it's going to be orange like this and the value is not going to be 500 but it's going to be 700 like this so it looks the same as your sidebar and as this tools component right here great now let's go ahead and let's change the placeholder for the input instead of saying uh piano solo we're going to write a more realistic prompt something like clown fish swimming around a coral reef like that there we go and let's go ahead and modify we're not going to store any music here instead we're gonna store video so let's change let's first change the music page to video page like that and then let's change the music state to video and set video like that now let's modify our on submit function so it's going to be set video like this and this is going to be set video as well and let's also modify the API request so it's going to go to slash video and not slash music and the response data is going to be a little bit different so we are actually going to go ahead and pick the first from the array because that's what is going to be returned from the API so make sure you modify it from data dot audio to data first in the array right here great and now let's go all the way down here so we can actually fix these errors so instead of checking for music we're going to check for video and we're gonna write no video generated like that and the same thing for the yes we're gonna add video and we're actually not going to render an audio file we're going to render a video file like this and we're going to give it a class name of w Dash full aspect Dash video margin top 8 rounded LG border and BG Dash black like this and I also want to give it a controls prop so we can control the video and inside add a source element with Source video like that so very similar to music great and now let's go ahead and let's actually create this slash API video because the prompt can stay the same so if you check the constants right here you can see we have a form schema and let's just change the message to video prompt is required but again you actually don't need this at all because you're not displaying it anywhere great and now let's go ahead and close everything and let's go inside of our API and let's copy the music folder again let's paste it inside of the API and let's rename it to video and in route.ts let's go ahead and let's see what we have to change here so first things first we have to go inside uh replicate again and we have to find the AI model we want to use so in my case you can play around you can choose whatever you want really there are many models here the one I'm going to use is going to be zeroscope right here but if you go into collections you can go ahead and find uh this videos collection and you can see that they have a bunch of video models that you can use and you can feel free to play around with them but the reason I chose this one called the zero scope so go into Explorer and it should be the very first right here or you can search zeroscope like this and find it the reason I chose this one is because it has the lowest uh a time to generate everything else is like a thousand seconds this one seems to be the fastest so that's why I chose this one great so if you want to you can play around and test it but be careful if you have a free tier you don't want to block yourself from using the tutorial and have to add a billing so let's just go into API right here and let's go ahead and let's copy this output right here so we know what to put in our API so I'm going to copy this output from here I'm going to expand this and I'm going to replace this response with that but I'm going to change the constant to be response like that and you can see how I'm using xeroscope right here so this exact problem that I copied from here great and let's change this prompt to actually use our prompt so you can either use prompt like this or you can use a shorthand because it's named exactly the same and we're extracting prompt from the body so we don't need to change absolutely anything everything is fine except we have to change the music error to video error like that and sending just response is fine great and I think we are ready to immediately test this so let's go inside of our genius right here I'm gonna go ahead and write clown fish swimming around a a coral reef like that and I'm Gonna Leave it again I assume it's going to take a long time to generate because this is another called the start so be patient my music took 12 minutes so I assume this is going to take something like that so just be patient and I'm gonna unpause the video once it is ready and there we go it has generated for me and this is how it looks really really cool it's so cool to me that an AI generates this realistic looking videos and you can even test the prompts more for example you can see in this demo right here that they give it a very specific and very um detailed prompt so you can play around with that to get a better result and let me show you exactly how long it took me to generate this so I'm going to zoom out a bit so it took me 42 seconds to generate it um so yeah it was a bit shorter than this one this one says 28 seconds but you know that the cold start was 12 minutes this one was actually much faster for me but if yours is still running as long as you can see it on the dashboard zeroscope that means everything is fine you successfully finished every single AI tool great great job uh what we're gonna do now is we are going to implement subscription and API limits to how much free users can use our application and then we're going to add crisp for customer support and then we are going to implement the landing page so now that we have all of our AI tools working it's time to create a functionality that is going to limit free users to only be able to use them five times in order to do that we're going to use Prisma so let's go ahead and let's set up Prisma first I'm gonna go inside of my terminal right here I'm gonna shut down everything and I'm gonna write npm install Dash capital D Prisma like this I'm gonna wait a couple of seconds for this to install and then we're gonna run Prisma in it so it's going to create a couple of files in our project so let's go ahead and let's run npx Prisma in it like this and there we go we have our prism setup let's take a look at everything that has been created inside so we have a new folder called Prisma and inside we have schema.prisma right now it's set to postgrad SQL we're going to change that to mySQL and we also have some additions to our DOT environment file you can see that this was inserted by Prisma in it and we have a database URL which is currently a postgresql database0 which of course is not working it's just a mock URL so we're going to change both of those in order to do that we have to create our planet scale account so let's go ahead and go to planetscale.com or Google Planet scale and find sign in or get started button so I'm going to go ahead and log in and show you what it looks like great so once you're here if you want to you can go ahead and click click on all of these different options or you can simply go ahead and click on this create option right here so just click create and let's go ahead and let's give this database name AI SAS like this and you can go ahead and select a free option right here and then just go ahead and click create database and just confirm that your total monthly cost is free I just want to ensure that this looks like it to you so just select the free option right here so it's you don't have to pay anything for Planet scale for your uh one database that you have great and this can take uh some time so I'm gonna pause the video and I'm gonna show you what it looks like once it's initialized so again just be patient and you're actually going to see a little pop-up here saying that you are ready to connect so just be patient and wait for this to finish and you can see how I have this prompt ready to connect to your database and I have an option to click get connection strings so click on either of those I'm going to use this really to connect to your database and in here you can see that we have to click create a password like this and there we go once this has created you can choose how you want to connect go ahead and select Prisma because that's what we're using so first thing you have to do is copy this database URL and let's paste that in our environment file right here so I'm going to replace this entire thing that has generated by by Prisma and paste this new string right here using Planet scale right here so P scale and you can see the name of my database right here great and what we have to do next is modify schema.prisma so click on this tabs schema.prisma copy the entire thing and let's go back inside of our code go ahead in schema.prisma which is in your Prisma folder and you can just paste it here and you can see right now it uses the MySQL provider and reads the environment while database underscore URL which we've added right here alongside everything else we have great so that is actually it for Planet scale you can safely close that now uh that is ready and let's go back and work on our application so one util I want to add before we continue uh is our Prisma DB client so let's go inside of our terminal and let's run npm install at Prisma slash client like this wait a couple of seconds for this to install and then we're gonna create a util which is going to be able to access the database using Prisma and this package so there we go it's installed and now let's go inside lib and create a new file called Prisma db.ts like that go ahead and import Prisma client from ads Prisma slash client and declare global war Prisma to be Prisma client or undefined like this and then just write const Prisma DB is equal to globaldis.prisma or new Prisma client like that and then we're going to write a condition so if process.environment dot node environment is not identical to production meaning its development in that case we're gonna assign Global this dot Prisma to Prisma DB like that and just export default Prisma DB like this so what does this code do and why do we need this declare Global well you can try and remove it and then you see you have a curly line in Globe of this so that's why we need this we add Prisma variable to our Global window great and why are we doing this uh conditional why are we checking for Global this and why don't we just do new Prisma client that looks much simpler well that's because the way next 13 does reloading uh it basically there is a chance that you have a lot of Prisma clients initialized every time you hot reload every time you change something in your code so this way we assign that in not in production so in development we assign this to Global this that way it's not affected by hot reload and you don't get that warning in your console that there are multiple Prisma client instances active great and now that we have that we can actually go ahead back in our skin Prisma and let's create the model for our user API limit so one thing I just want to tell you right ahead if you are seeing this Prisma a bit different so if you're not seeing blue colors for the generator client and clearly separate the strings so if everything is in the same color for you make sure that you're using the Prisma extension so just type in Prisma it's the first one so make sure you install that and use that because it also has some autocomplete features great now let's write our model so model user API limit like that let's give it an ID which is a type of string decorator ID and a default value is going to be cuid like this user ID is going to be a string and you need pound is going to be an integer and it's going to have a default value of 0. created at is going to be a date time with the default value of now and updated ad is also going to be a date time with a decorator updated at like this perfect now that we have that let's go ahead and let's try and push that to our database so I'm going to write npx Prisma DB push like this and let's see if we this is going to work or if we are going to get any errors so you can see that we are using Planet scale and this is the name of my database and mine was successful so now it's successfully uh in the database and every time you modify your schema Prisma you have to push it and you also have to run npx Prisma generate like this so npx Prisma generate is going to add this model to your node modules so then you have the types and you're not going to get any errors while developing great now let's go ahead and let's go inside of actually before I run the app I want to show you how you can take a look at your data so I'm going to run npx Prisma Studio like this and I'm gonna add it right here okay I'm just gonna copy it like this so I'm gonna paste it here there we go and now you can see all of your data inside so you can see this model that we just created user API limits and once I click here we have no uh API limits inside so now we're slowly going to create some utils and we are going to create it first for the conversation model so we're going to start by protecting the conversation model and every time we add a new prompt in here in conversation uh we're gonna create a new account for user API limit and that is the way we are going to protect the maximum count the maximum number of times free tier users can use this conversation model so let's go ahead and do that so you can keep this Prisma Studio running in another terminal if you want to I'm going to shut it down and I'm going to run the application instead so I'm going to refresh this I'm going to zoom out just a bit and I'm going to wait for it to load and what we're going to do now is you're actually going to go ahead in our constants file which we don't have yet so let's get at the root of our application and create a new file called constance.ds like this and go ahead and write export const Max underscore free underscore counts to be five you can of course change this to whatever you want so for this tutorial I'm gonna allow free theater users to use the app five times you can increase this to 100 if you want to and now let's go ahead inside of our lib folder and I'm going to create a new file called api-limit.ts like this and let's go ahead and let's import out from clerk next.js like this let's import Prisma DB from dot slash Prisma DB because it's in the same folder or you can use slash Libs lib like this and let's also import Max underscore free Counts from add slash constants great so now let's write a util that is going to increase our API limit count every time we ping one of the apis so export const increase API limit is going to be in a synchronous Arrow function we're going to extract the user ID using auth like this if there is no user ID we're just going to return and break this function now if there is what we're going to do is write const user API limit like this to be a weight Charisma DB dot user API limit if you did not get this autocorrect or if you're getting an underline in here make sure that you run npx Prisma generate and refresh your application and reload your entire vs code so you need to run npx Prisma generate and npx Prisma DB push and this user API limit is matching what we have in our schema so make sure you didn't misspell this accidentally user API limit and then it should work in here as well so first thing that we're going to do is we're going to check whether there is an model there is a document in the table for this exact user for its API limit because right now when we register this model doesn't exist in my Prisma studio so first thing I'm going to do is check if it already exists so I'm going to write find unique I'm going to open an object and like where user ID is equal to user ID or you can use the shorthand so we're going to fetch that by looking at the user ID in that model and we get the user ID from the app like that so if there is user API limit so if it already exists in that case we are going to update its count so I'll wait Prisma db.user API limit dot update where user ID is equal to user ID and data is going to be count user API limit dot count plus one like this great else we're going to create a new user API limit so we're going to write a weight Prisma DB dot user API limit dot create data user ID is going to be user ID and count is going to be one like that beautiful and now that we have that we can go ahead and create another YouTube which is going to check whether the current user has reached the limit of their free usage so export cons check API limit is going to be in a synchronous Arrow function we're going to extract the user ID again so out like this if there is no user ID we are immediately going to return false so we are not going to allow the user to go any further and then let's go ahead and fetch the user API limit so const user API limit await Prisma DB dot user API limit dot find unique where user ID is equal to user ID like this if there is no user API limit or if there is user API limit and the current account is less than Max 3 counts so if the user has not used up all of their accounts or if they never even created their first generation meaning that they don't have this user API limit in that case we're going to return true meaning they can still use our app but if user has passed the free count in that case we're going to return false meaning that we have to block the user from using our API great and now that we have these two we are ready to add them to our routes and actually protect them so let's go ahead and do that next so let's go inside of our conversation for example so I'm gonna go into app API inside conversation route CS right here and I'm going to go ahead and I'm going to import increment API limit and check API limit from add slash lib API Dash limit like this and after I check for this messages right here what I'm going to do is I'm going to check whether we are on a free trial so it cost free trial is equal to 08 check API limit like this great and now if we have passed the free trial so question mark free trial in that case we're going to return new next response free trial has expired like this and let's give it a status of 403 so the status of 403 is really important because the status this is the status that we are going to recognize on the front end and know okay we have to trigger a pro subscription model so make sure you return a status of 403 in this case if there is no free trial available great and in case this passes we're going to normally generate the response and then we're gonna run a weight increase API limit like this great so let's go ahead and test this now but the best way to test this is to keep your Prisma Studio running in the same place so I'm going to go ahead and click plus right here so I have two Terminals and I'm going to run npx Prisma Studio like this and now it's running right here great so I have both of my terminals running now and let's check this so make sure that you are in your conversation right here make sure that you have nothing inside of your user API limit so select user API limit from here great and let's check now so I'm going to write I don't know hello how are you like this let's wait for the response from chat GPT great so it has responded and let's see if I refresh right now I think I should have great there we go you can see that I have my user API limit and it's linked to my clerk user ID and the count is one so let's try another one this is a test let's see if that is going to increase our limit let's go ahead and refresh again there we go count is now two so I'm gonna just zoom in so you can see count is two and I'm gonna go ahead and just try a couple of more times until we reach uh the count of five and see what happens then let's see so I just pasted a couple of messages I'm gonna refresh there it goes now it says that count is five and if I'm correct we should now get an error saying that free trial has expired so I'm gonna go ahead in my network right here and I'm gonna write expired and click generate and there we go you can see this time it didn't work and we got a 403 that free trial has expired great great job you officially implemented API limits for your users what we have to do next is we have to connect them with stripe to actually check uh whether the user is in Pro or not but we're gonna do that slowly what I want to do now is I want to add this code for uh checking free trials into all of our routes so let's go ahead and let's copy this and let's go inside of code next right here so I'm going to add that here inside of code like that I'm gonna go back inside of conversation and I'm gonna check for free trial and the if clause for the free trial and I'm gonna paste that after this messages right here and then all I'm gonna do is just await increase API limit after I give the respond so make sure you're doing this in code route great so now that we have that copy this again and let's go into the next one so we did code and conversation now let's do image right here so I'm going to add it right here let's go ahead and let's copy this check for the free trial like that so after we check for resolution let's do it right here and after we do the response our weight increase API limit like this great so copy this again let's go to the next one let's go to the music right here let's paste this like that let's copy this uh code for checking the free trial and after the prompt go ahead and paste it here and after the response I'll wait increase API limit like that perfect go ahead and copy this one more time and let's put that into the video as well so after the prompt check whoops my apologies so first we have to paste this Imports and then let's go ahead and copy this free trial check and after the prompt let's give it right here there we go and of course let's not forget to increase the API limit so I'll wait increase API limit like that perfect so now every single one of our uh AIS is protected so if I try image generation for example it will immediately fail you can see that it's not generating any anything and the same thing is true for video yes it's failing too and piano as well and code generation as well so all of them works and if you go in Prisma studio and modify this account to I don't know three and click save changes and then go ahead and try conversation it should work normally because our Max count is five and you can of course change that in the constants file so you can change Max free counts to 10 if you want to great great job uh what we're going to do next is we're gonna go to the front end and add the counter here into the sidebar and then we're gonna go ahead and handle the 403 error in each of our components here so we can display a nice model let's build the front-end interface which is going to show the amount of three generations we have in this sidebar right here so let me just remind you what I have opened so in my terminals right here in the first one I have the app running and in the second one I run npx Prisma studio so make sure you run this Command right here and make sure you visit Prisma Studio on localhost 55555 so I have that opened right here in my second Tab and as you can see right now I'm on count four so it doesn't matter what count you are on but just go ahead and reset this to zero and click save one change so you can develop the same way I am so just refresh again make sure this is zero and refresh here as well great so what we are going to do now is we're going to add another action inside of our API limit lib so going to the lib folder and go inside API Dash limit right here I'm gonna expand this and alongside our check API limit and increase API limits we're gonna add another one and this is going to be called get API limit count so export const get API limit count like this is going to be in the synchronous Arrow function which is also going to use user ID from out whoops we already have it so no need to import anything we already have this out from Clark next JS great if we don't have a user ID in that case you can just return zero uh and now we're gonna go ahead and try and fetch the user API limit model so const user API limit is equal to await Prisma DB dot user API limit dot find unique like this where user ID like this great and if there is no user API limited that means the user has never run a generation before meaning that we can also return zero because they have no they haven't used any of their free counts otherwise you are going to return user API limit dot count like this great and now what we have to do is we have to run this action inside a server component so we can pass it in our sidebar component which is a client component so let's go ahead let's go inside of our app folder inside dashboard route group and go ahead and select the layout inside the dashboard folder right here so make sure you are in dashboard layout and not in any other layout because we have two layouts so make sure you're not in this one go ahead in the dashboard folder and select that layout right here now let's go ahead and let's add this so const API limit count is equal to a weight get API limits count like this and make sure you import get API limit count from add slash lib API limit and we have an error because we are trying to use a weight inside of an arrow function which is not a synchronous so just make sure you mark this as an asynchronous layout like this and now we're going to use this API limit count and we're going to pass it into the sidebar as a prop so go ahead and write API limit count with a Capital C like this and just paste that here and Save we have a type error here because our sidebar component does not accept this prop so go ahead inside of your components folder inside sidebar right here and let's go ahead and let's modify our interface actually create our interface so you can accept that uh here so let's go ahead and write the interface sidebar props like that and just write API limit count to be a number like this and then go ahead and extract API limit count from our props and just let's give it a type of sidebar props like that and you can give it a default value of zero so it's easier to work with great now that we have that what I'm going to do is I'm going to go all the way to the bottom and I'm going to find this last div here and before this all ends I'm going to add a new component called free counter like this so it's a component that we don't have yet it's going to be a self-closing component and we're going to create it in a second but first let's go ahead and pass API limit count to it right here so what we are doing is we are fetching the API limit count in a server component because our server components have access to Prisma right here and then we are using that to pass it as a prop to sidebar which is a client component so that's why we need to do that that's why we could not fetch it inside of here and then we are using it from here to pass to pass it to this new component called free counter so let's go ahead and let's create that component now so inside of your components folder create a new file called free Dash counter.dsx like this let's go ahead and write export const free counter like this and return a div saying free counter like that now you can go back inside of your software component and import the free counter from dot slash free counter like this or you can modify it to be from components like this so I'm just going to move this right here and I'm also going to move my icons up here with the Global Imports there we go so now you should not have an error beside this type error right here and if you expand your sidebar you should see the text free counter in the lower bottom corner great now I'm actually gonna try and keep my sidebar expanded like this so make sure you're on desktop view when you're working with this later on we're also gonna have to add that to our mobile sidebar but for now we're gonna wrap up the desktop so make sure you are seeing this on desktop view great now let's go back inside of our free calendar and let's create an interface for this so interface free counter props is gonna take API limit count which is a type of number as well like that let's go ahead and let's extract AKA limit count like that and let's give it a type of free counter props and give it a default value of zero like that now let's go ahead and do a simple trick which is going to prevent this from causing any hydration errors so const mounted set mounted is going to be use state from react and default is going to be false so we automatically need to mark this as a client component like this but it's also not going to throw any errors if you don't write use client the reason for that is because we are only using this free counter component inside another client component so when you add a component to a client component and don't explicitly mark it here it's also going to be a client component but if I were to put this inside a server component without this sign it will be a server component and this is going to throw an error so that's why it's still safe to mark this as use client great now let's go ahead and write the use effect here also from react and we're just going to set mounted to True like that and if we are not mounted in that case just return now so this way this is not going to be rendered on the server meaning that it's not going to cause any hydration errors great now let's go ahead and let's actually style this so I'm going to give this div a class name of PX3 like this and inside I'm going to add a card element from dot slash UI card or you can move that to slash components like I'm going to do there we go so now we have the card ready here let's go ahead and give this card the class name of bg-110 and Border Dash zero great and inside I'm gonna create a card content component so I also imported that from slash components UI card let's give this a class name of py-6 like this and now you can see how I have a small rounded element right here in the lower corner great now inside I'm going to create a div with a class name of text Dash Center text SM text white margin bottom four and space Dash Y dash 2 like that and inside of that div I'm going to go ahead and open a paragraph where I'm going to go ahead and write the current API limit count like this which you can see is zero right here the reason it's zero is because that is in my database right here it's zero so you can see how it changes once we increase it I'm going to write API limit count I'm gonna add a slash and now I'm gonna render how many accounts are available so for that we're going to use our constant Max underscore free counts so make sure you import that from add slash constants like this so now it's going to write 0 out of five right here in your corner and just add a text three generations like this so now it says 0 out of five three generations great and what we have to do now is we have to add another component from chat CNN UI so let's go ahead let's visit the documentation right here and go into components and find the progress component right here as you can see let's see how we can install that so using this npx chat cnui at latest add progress I'm gonna copy this as npm I'm gonna go ahead in my terminal right here I'm going to shut down the app for now and I'm gonna go ahead and run this Command right here so I'm just going to expand so you can see npx chat cn-ui at latest add progress right here and confirm the installation after that has been finished go ahead and run the app again so I'm gonna close this I'm gonna expand my screen a bit and I'm going to refresh my application here great now that we have that just below this paragraph right here go ahead and add progress from dot slash UI progress so don't accidentally import it from Radix you need to import it from dot slash UI progress or you already know what I'm going to do I'm going to rename these two components because I like to be consistent great now that we have the progress let's go ahead and let's give it some values so it's gonna have a class name of h-3 so I want it to be a bit thinner and I'm going to give it a value of API limit count divided by Max three counts times 100 like this beautiful and one more thing I'm going to do outside of this div encapsulating our paragraph and our progress but still inside of card content is I'm going to add a button component from dot slash UI button and you already know I'm going to move this to components like that there we go so now we have the button right here and let's go ahead and write upgrade right here and let's give it an icon of Zap from Lucid react so make sure you import zap from lucidreact and I'm going to move this to the top right here great let's give this zap a class name of W-4 h-4 margin left Dash 2 and fill Dash white like that beautiful and now what I'm going to do is I'm going to give this a class name of w Dash full and one thing I want to do is I want to make this upgrade button look a bit special I want to give this a gradient I want to make this look more I want to make this look different than all the others buttons right because this is our upgrade button so what I could technically do is just modify the background in the class name but since I want to reuse this button in our model as well I think it's cool that we utilize the fact that this button component is actually inside of our components folder and we can freely add our own variant to it so let's see how we can do that I'm going to close everything and I'm going to go inside of my components folder inside UI and I'm going to find this button.dsx so we already took a look at this when I added it in the beginning of the video but what we're going to do now is we're going to add another variant right here so the same way as default destructive outline secondary ghost and Link exist we're going to go ahead and after the link or whatever is your last one go ahead and add premium or whatever you want you can name this absolutely anything and let's go ahead and give it a value of BG Dash gradient Dash 2 right like this let's give it a from Dash Indigo 500 via Dash purple Dash 500 to dash pink Dash 500 tax dash light and Border Dash zero like this so I'm gonna expand this so you can see how it looks in one line uh even more okay I can't expand it that much but I can do this so VG gradient to write from Indigo 500 via purple 500 to Pink 500 text white and Border zero like this and now let's go ahead back inside of our free counter component right here expand this so you can see the sidebar on desktop and go ahead and give this button a variant of Premium you can see how it also Auto completes and once I save there we go you can see how premium this button now looks and now let's actually go ahead and check if this accounter is working so I'm gonna go ahead and refresh everything and I'm gonna go ahead and well I will repeat one of these questions it doesn't really matter and let's see if this is going to increase right here so genius is thinking and there we go it says that I've used up one out of five of my three generations and my counter right here is starting to fill but why is this working how did it automatically refill well let me explain that to you so what are we doing where are we fetching this information we are fetching that inside a server component called layout.tsx right here so we are calling awaiting API limit count that the only thing that that is doing is calling Prisma so how did it know to refresh once I did this generation what made it uh update the values right well let me explain what let's go inside of the app folder dashboard routes conversation page.tsx right here and let's take a look at what we do once we submit so you can see that in the try block we actually call the API request and then we just assign the data so nothing in here actually refetches the server component but in our finally block this is what I told you I was going explain later this is router.refresh so router.refresh is used to rehydrate all server components fetching the newest data so once you do router.refresh it doesn't really matter where you are all server components are going to get refreshed with new data from the database so that's why I think it's really cool to work with server and client components in extra thin perfect so that's why it works so if you don't have this in your final block you're probably not getting any updates so make sure you have this in every single on submit in our uh in all of our apps right here but since we copied and pasted the code I have it everywhere great amazing job we of course could change this color a little bit I think it looks kind of weird so let me see if we can do that quickly so what we can actually do is we can change both this progress color and the color of our primary buttons in a color that you saw in the intro video which is a light purple color so in order to do that let's go ahead and let's close everything and let's go inside of our app folder and find globals.css right here now inside the layer base root right here go ahead and find the dash dash primary so this is an hsl value right here and I'm going to modify it to the color that I like so I'm going to remove all this in primary and I'm going to write 2 4 8 90 and 66 percent like this and I'm going to save and look at this now you can see that my button is light purple and you can see that my progress bar is light purple as well perfect so that's exactly what I wanted beautiful job you just finished the counter and now what we're going to do is we're going to add a pro model which opens and then finally connects to stripe now let's create our premium model which is going to open when we click on upgrade or when we fill up our generations and then get a 403 error when we try a generation similarity to do that first we have to install a package called to stand so let's go ahead and let's go inside of our terminal right here and go ahead and run npm install to stand like this wait a couple of seconds for it to install and run the app again and refresh if you have shut it down and now let's go ahead and create a hooks folder in the root of our application like this and inside create a new file called use Dash Pro Dash model dot ESX like this go ahead and import create from not from domain but from to stand and just let's go ahead and write an interface for our model so interface use pro model store like this it's gonna have is open which is a Boolean on open which is a void and on close which is a void as well and now let's go ahead and write export const use pro model to be create go ahead and open pointy brackets use pro model store go ahead and open parenthesis open parenthesis again write this set prop write an arrow function which is going to immediately return an object like this it is open default value to false on open is going to be an arrow function which is going to call the set prop which we extracted right here and it's going to set an object is open to be true like that go ahead and copy and paste that rename this to on close and this one is going to set is open to false like this so this gives us Global State controls for opening and closing the model from wherever we want now let's go ahead and let's create a provider for our model because it's going to have to be rendered uh carefully so we don't cause any hydration errors so in components right here create a new file called Model Dash provider.tsx like this and inside go ahead and Mark this as use client that's important and Export const model provider like this go ahead and add is mounted set is mounted from US state which is from react give it the default value of false go ahead and add use effect like this and set is mounted to True inside and if it's not mounted in that case return null like this otherwise return a fragment and let's go ahead and add pro model inside which of course does not exist yet so let's go ahead and let's quickly create our pro model so inside of components create a new file called Pro Dash model dot vsx export const pro model like this and I'm just going to return hey div saying pro model like this for now and go ahead and Mark this as use client just to be safe and now go back inside the model provider and import the pro model from dot slash pro model or slash components pro model like this now we have to add this model provider to our layout application so let's go ahead and let's go inside of a layout of our app so not inside dashboard but inside of our app right here so this main layout root layout file and inside of the body let's go ahead and collapse this and just above the children let's add model Provider from dot slash components model provider like this there we go so now we have the model provider here and you can see the text pro model right here we are of course going to modify this to actually work as a model so in order to do that we can we're going to have to go and add another chat CN component and that component is going to be called dialog right here so you can see how this is going to look like great so this is the command we are going to need MPX chat cnui at latest add dialog I'm going to copy this as npm go inside of my terminal and I'm gonna run npx chat cnui at latest add dialogue like this so just press enter and confirm this installation and then you can go ahead and run your application again great so I'm gonna expand this and I'm gonna refresh this okay and now let's go ahead and let's replace this div with dialog component from dot slash UI dialog so make sure you don't accidentally import this from Radix like this and you already know I'm gonna rename this to slash components there we go and now let's give it the default value of open like this and now let's add dialog content from add slash components dialog so again make sure you have no Radix Imports inside I'm already going to collapse this so they all Import in that manner and inside of dialog content add a dialog header also from UI dialog like this and inside add a dialog title from components UI dialog again and give this a class name of flex justify Dash center items Dash Center Flex Dash call Gap Dash y-4 and padding bottom to like this and let's just write upgrade to genius like this and there we go now you should see a model right here but you can see that our sidebar is actually uh higher of a higher Z index than our model so let's quickly fix that by going back inside of our sidebar component right here and let's see what's going on here uh or maybe it's not the sidebar I think it actually might be our layout file so go ahead inside of dashboard inside layout right here and there we go so we put the Z index but I think we don't actually need it so yes you can see how now the model is above our layout right here great so I'm going to use that and now go safely back inside of the pro model right here and before we do anything let's actually connect this to our two-stand store which we created so go ahead and let's add the pro model controls here so const pro model is equal use pro model from add slash hooks use pro model and you're going to change this hard-coded open to be pro model that is open like this and you're gonna change on open change to be promodel dot on close like this great so now you can go ahead and go inside of your use pro model for example and change the default value of is open to true and that is going to open your model so just do that for now so your model is visible so you can see what you're developing if you change it to false it's going to go away so just bring it back it sure you can even refresh if it's not working great so now that you have that and we have these controls here let's go ahead and let's add another Library sorry another component from chat CN called batch so I'm going to go ahead add inside batch right here and I'm going to add npx chat cnui at latest add batch so I'm going to expand this a bit more so terminal is better of better visibility and there we go so ntx chat scene UI at latest add batch like this and just confirm the installation and run your app again great so I'm gonna expand this I'm gonna refresh this make sure the app is running uh correctly there we go so now alongside this upgrade to genius just below that add a batch component from dot slash UI batch and I'm gonna rename that to slash components badge like this and inside of that Badge go ahead and write Pro like this and now let's go ahead and give the class name of uppercase Dash text Dash whoops uppercase like this text SM and padding y one like this great and now what I want to do is I want to create a div right here encapsulating both of these items here and I want to give this a class name blacks items Dash Center like this I'll make sure it's class name like this so Flex items Dash Center Gap Dash X-2 font dashboard py dash one like this there we go and I want this badge to be of a gradient similar to what we have inside of this upgrade button so I want it to look similar so let's go ahead and do that so just refresh your page if you close the model and make sure that your default value here is true we are of course going to change this to false later but just for development part it's easier to do it like this so what we're going to do now is we're gonna go inside of our components UI and find the batch component right here and the same thing we did with the button we're gonna add another variant Hill here called premium like this it's going to be BG Dash gradient Dash 2 Dash R from Dash Indigo Dash 500 via Dash purple Dash 500 102 Dash pink Dash 500 and text that primary Dash foreground like this uh and also border Dash zero and now let's go back inside of our pro model here and let's give this badge a variant of Premium like this there we go now it looks much much nicer so basically in the badge we added the same value we have in our button component right so this is the premium variant that we added right here and then we used it in the pro model variant premium like this great great job now let's go back and let's focus on the pro model here so let's add some content inside so just outside of dialogue content right of dialog title right here let's go ahead and add dialog description right here and yes I accidentally imported it from Radix dialog so make sure you don't do that you have to import it from components UI dialog like that so dialog description component here inside uh give you the class name of text Dash Center pt-2 space Dash y-2 text Dash sync-900 and phone Dash medium like this and inside we're going to iterate over our over our tools so we're going to get the tools from the dashboard so go ahead and find the dashboard page so routes dashboard page.tsx and you can either add this to Global constants or copy it you know you can find a smarter way if you want to but I'm just going to go ahead and copy this tools constant for now so make sure you are in dashboard routes dashboard page find the constant tools copy this and you can just paste it inside of the pro model just like this right and I'm also going to copy all of the icons from this page right here so I'm going to copy this imports from lucidreact and I'm gonna go ahead and paste them here and the only one we don't need is Arrow right like this and we're actually not gonna need the href at all in any of this because we're not gonna redirect from anywhere we're just gonna display all of the tools available in Pro of our application great so inside the dialog description now go ahead and write tools.map individual tool like that and let's go ahead and add a card from that dot slash UI card or rename it to slash components like this great let's go ahead and give it some properties so key is going to be tool dot href like this uh sorry not href it's gonna be uh dot what is the is it label yes like this uh and let's add a class name here p dash three border Dash Black slash F5 Flats items Dash Center and justify Dash between like this great inside of that create a new div with the class name of flex items Dash Center and GAP Dash x-4 make sure you don't misspell class name like I did great inside of that create a new div with a class name which is going to be dynamic so CN make sure you import that from s slash lib utils so as I did right here we have the CN here open parenthesis let's write a default class of w 6 and H uh my apologies now uh the first Dynamic class is p-2 W Dash fit and rounded MD and the dynamic class is tool dot PG color like that and then inside we're going to go ahead and render tool dot icon which is a self-closing tag and give you the class name which is also Dynamic so cnw-6h-6 and dynamic is going to be tool dot color like this there we go so now we have all the icons of our tools here great and outside this div encapsulating our icon create a new div and render tool.label inside let's just go ahead and give this a class name bold and text SM like this great and outside of this div right here but still inside of the card we're gonna add an icon check from Lucid react so the check should now appear at the end so make sure you've imported check from lucidreact alongside this Imports which we copied from the dashboard and let's give this a class name of text primary and W5 and h-5 like this there we go great now that we have that let's go ahead and add dialogue footer from add slash components UI dialog so make sure you have imported that right here uh and let's go ahead and add a button from dot slash UI button or you can rename it to component Cy button like this and inside we're gonna write upgrade like this and we're going to add a zap icon from Lucid react so the same thing with it in our sidebar like this give it a class name of W-4 h-4 margin left 2 and fill Dash white like this and now let's go ahead and give some props here so size is going to be large variant is going to be premium and class name is going to be W Dash full like this great and what we are going to do now actually uh well we're gonna go ahead and we are going to change the way we open the pro model so now we can close everything and go inside our use pro model store and change the value of ease open to false like this and let's find proper ways to open this model so the first thing I want to do is I want to open it when I click upgrade in the sidebar so for that let's go inside of our components not inside the UI so I'm going to close everything so it's easier and I'm going to try and expand this so just be patient for a second all right so inside of components inside of free counter right here we have this button which says upgrade so what we are going to do is we are going to add a constant pro model to be use pro model just above our mounted U State here and make sure you import the use pro model from add slash hooks use pro model like this great and now that you have that pro model you're going to add an on click here to this button to say pro model dot on open like this and now when I click on the upgrade you can see that it shows this upgrade to genius right here great and now what we have to do is we have to add the pro model to open every time we capture a 403 error so let's go ahead and do that now so there are certainly different ways you can do this you can find uh one middleware which is going to look for four or three errors but let's do it the simplest way for now I'm gonna go inside of my dashboard I'm gonna find the conversation app first I'm gonna go inside of page.tsx and there we go we wrote it to do here open pro model so we're going to search through all of our to-do's and we're going to add open pro model wherever we need to do that so what I want to do now is in this catch form we have to check if the error is a 403 three so I'm going to remove this and I'm going to remove this as well and I'm going to write if error question mark response question mark status is identical to 403 in that case we're going to open the pro model so first we have to add the pro model so just above the router const pro model is equal use pro model from ad slash hooks use pro model like this and now in here in this F block just go ahead and run promodel dot on open like this great and make sure you add question marks here because errors can be uh in we don't have a guarantee that it's always going to be this 403 error which we are expecting it can be any error and it could be uh it could have a different structure so that's why I'm putting question marks because this is one of the options that I know might happen and if that option happens we have to open the pro model so let me just explain when this happens so if you go inside of your conversational route you can see that if you don't have a free trial in that case we throw a next response error with a 403 status so that's why it's very important that you put a 403 here please make sure that wherever you do uh in the in this if block if there is no free trial throw a 403 error that is very very important so now let's go ahead and let's try and fill this up so I'm just gonna ask you the couple of questions so it fills up this three generations right here on the bottom until we get until we get to uh five out of five or you can simply go to Prisma and increase it here as well if you want to this seems to be taking uh each time so I'm gonna go ahead and do that so in here I'm going to increase it to five out of five and click save and now each of my requests should throw an error so you can see I have a 505 here I'm going to go ahead and write test inside and there we go so we caught the 403 error and we opened a model so if this is not working for you let's go ahead and check everything you need to do first of all make sure that you are in the dashboard routes conversation page.tsx make sure that you are in the unsubmit function make sure that you catch the error and that you check for error.response DOT status 403 next make sure that in the equivalent API sorry this is for code so the equivalent route conversation route.ds make sure that you're actually throwing a 403 status if there is no free trial there we go so you can see how amazing this looks perfect and now we have to do that for all others as well so the simple way we can do that is if you copied and pasted the code we did I'm just going to search for to do like this and you can see that I have all the places where I need to add a pro model but in case you didn't add the comment don't worry I'm going to show you exactly where this is so let's see click on this one and I'm gonna show you exactly what that is so go inside of your dashboard routes code page.dsx right here and I'm also going to open the conversation just so I can copy and paste stuff so inside of the cache function I'm just going to copy this if Clause right here and I'm going to replace this console log and this right here and of course we have to add the pro model so let's go to the top of the code page right here at const pro model to be use pro model from hooks use pro model like this let's go ahead and let's check code generation now so if I try something inside there we go I have a model here as well perfect now let's go ahead and I'm already gonna copy this okay let's go ahead and go to the next one so this is routes uh which one is it uh where are my image okay so this is routes image page.tsx go inside of here replace this there we go let's add the pro model so const like this there we go let's test out the image generation works as well great now uh let's do either video or music generation so let's see but we are here's some music let's do music first so same thing here uh I'm just gonna copy and paste it from here so remove the comment and the console log in the catch block and add the pro model to the top so conspro model is use pro model like this there we go I'm already gonna copy it and let's just test if music is protected as well so I'm gonna write anything here there we go the pro model has opened and now let's go ahead and finish the last one which is video route so go inside of your uh video folder page.dsx right here and just go ahead and paste this here and add a pro model one last time there we go perfect and now if we test the video there we go works as well one thing that I think is not working is the mobile sidebar so if I test it right here there we go here it says 0 out of five regenerations but in desktop it says 5 out of five so why is that happening well that's because we did not pass the API account from our dashboard to our mobile sidebar so let's go ahead and let's see how we can fix that so what we have to do is we have to go inside of our components inside navbar right here and this is a server component meaning that we have we can fetch stuff from here and don't have to prop drill through dashboard the layout like we did with the sidebar so inside your navbar component where you hold the mobile sidebar and the user button so it's located here in components nav bar right here you can go ahead and write const API limit count to be await get API limit count from s slash lib API limit of course we have an error that we are using await in a arrow function which is not a synchronous so just Mark the snapbar as asynchronous like this now when we have this API limit count you can go ahead and pass it to mobile sidebar as API limit count like this now go ahead and inside of your mobile sidebar so mobile sidebar inside the components folder right here let's go ahead and create an interface so interface mobile sidebar props it's gonna API limit count which is a type of number let's go ahead and let's extract the API limit count and let's give it a prop type of mobile sidebar props like this and now that we have the API limit count we can just pass that to this sidebar inside of sheet content right here there we go and now our mobile sidebar works perfectly as well great great job what we are going to do next is actual stripe connection and the subscription creation so in order to integrate stripe first thing we have to do is create a stripe account so go to stripe.com or Google stripe and go ahead and find a way to log in or create an account so I already have that so I'm going to go ahead and just enter my dashboard once you're inside you're going to see a screen similar to this so what we want to do is you want to find this uh top right corner and you want to click create a new account right here or maybe this was what you initially did so if you already did that fine but since I already have this account my screen is a bit different so I'm going to name this AI SAS tutorial like this and I'm going to click create an account right here so now what we have to do is we have to copy our API keys so go ahead and click for API keys for developers or if you don't have this screen so let me just zoom in so if you don't have this screen API keys for developers you can just click for developers here and basically you can find it somewhere so there we go you have the publishable key and you have the secret key right here so you need to use this secret key so just click on this I and there we go click to copy like this and once you copied it so make sure it's the secret key right here copy that and then go inside of your dot environment file like this and you can paste it anywhere so at the bottom I'm going to write stripe underscore API underscore key like this there we go great so now that we have that uh leave this open because we are gonna need to create some web hooks later so don't close it just yet but we are not gonna do anything more for now great what I want to do now is I want to install a package for Stripes so go inside of your terminal and run npm install stripe like this there we go and run the application again now I'm gonna go ahead and I'm gonna create a live library for stripe so go inside your lib folder and create a new file called a stripe.ts go ahead and import Stripe from a stripe like this and Export con stripe to be new striped and instead inside you're going to use the process.environment of course you're going to use the key that you pasted here so stripe underscore API key and just paste it here like this and this gives an error so just go ahead and open an object here and to fix this error you can either use Pi pipe string or you can use exclamation point like this great so inside I'm going to use the API version of 2022 11 15 like this and typescript is going to be true like this perfect and now what I want to do is I want to create a route for my stripe but before I do that I actually have to go in my schema Prisma and create a new model for user subscription so let's do that first let's go inside the Prisma schema.prisma and the same way we did with model user API limit we're going to create a model user subscription like this we're going to have an ID of string which is going to be an ID and a default value is going to be CU ID like this we're going to have the user ID which is going to come from clerk which is string and unique we're gonna have a stripe customer ID like this which is going to be an optional string is going to be unique and we're going to map it to this name stripe underscore customer underscore ID so this is a way that we can access this both through this Pascal case actually I'm not sure sorry I said Pascal case but I'm not sure what's the proper name of this one so we can either access it using these underscores which is what stripe API uses or using this camel case that we are used to in JavaScript right here so that's why we are doing this map right here great and now we're going to repeat this for stripe subscription ID which is also a string unique map name stripe underscore subscription underscore ID and we have stripe price ID which is also an optional string it's not unique so this one is just map name stripe underscore price underscore ID and stripe current period and which is an optional date time and map that to name stripe underscore current underscore period underscore and like this perfect so let me just search for a stripe to see if we misspelled anything okay so stripe customer ID seems correct stripe subscription ID seems correct strike price ID seems correct and the map should be the same thing we have here but separated with underscores great so make sure you didn't do any misspells here subscription subscription customer customer price price everything seems fine current period And I think this is fine great and what do we have to do every time we modify our schema Prisma well we have to first shut down the application run npx Prisma generate so we add that to our node modules and then npx Prisma DB push so we push that to Planet scale and once you do npx Prisma DB push you can go ahead and run your application again if you want to you can reset the Prisma studio just in case so I'm doing this in my other terminal so I'm I'm running the app here but I run Prisma studio in my other terminal and now if you go here and if you close this user API limit you can see that we have user subscription as a model now as well so you can prepare that here right so just go ahead and select user subscription and there should be no results inside uh great now that we have that let's go ahead and let's create a route which is actually going to create our subscriptions great let's go inside of app folder inside of API right here and go ahead and create a new folder called stripe and inside of that create a new file route.ds like this let's go ahead and let's import out and current user from Clerk nexjs let's go ahead and import next response from next server let's import Prisma DB from lib Prisma DB let's import Stripe from lib stripe like this and let's import uh well one util that we have to create so what we are going to do is before remove this import for now and go back inside of your lib inside of eudels right here and go ahead and just write export function absolute URL that is going to be string like this and just return back decks like this go ahead and open the special object and write process.environment dot next underscore public underscore app underscore URL and then append the path from the prop right here so why do we need this what is this exactly well first thing let's add this to our environment variable so we can discuss it further I'm going to go inside of my DOT environment I'm going to add this and it's going to be HTTP localhost 3000 like this so make sure that you don't add a black backslash at the end and make sure that this is the actual route that you are running your application on so for me it's localhost 3000 it for you is something different then make sure that you use that great so why am I doing this well that's because what you will see that when we build this uh stripe route right here it's gonna need to take a return URL and the return URL cannot be just slash settings or slash dashboard it needs to be an absolute URL because stripe has no idea what application it's using or where it's hosted so that's why we need to give it an explicit absolute URL so it knows how to go back to our website so obviously in production we're gonna have to change this to our actual URL and now we can import that absolute URL from lib utils like this great and now let's create the settings URL so const settings URL is going to be absolute URL like this and it's going to go to slash settings like this with two T's so what this is going to generate is is going to call localhost 3000 slash settings so slash settings so just make sure that you don't put the slash here because then it's going to be slash slash settings that's not something we want so make sure it doesn't have a trailing slash at the end great and now that we have that let's go ahead and write export asynchronous function get like this let's open a try method inside and while we are already here let's do the catch first so catch error console log oops so go ahead and log stripe error like this and the error like that and return new next response internal error with a status of 500 like this great and now let's go ahead and let's uh first extract the user ID from out which we imported at the top uh and let's get the user from await current user which we also imported from clerk if there is no user or sorry if there is no user ID or if there is no user a return new next response unauthorized with a status of 401 like this great now let's go ahead and try and find current user subscription const user subscription is equal to await Prisma DB dot user subscription so again if you're getting a curling red line here or your Prisma did not autocomplete the user subscription make sure that you have the user subscription in your schema Prisma make sure you didn't misspell it make sure you did npx Prisma DB push and npx Prisma generate and if that is still not working you can just reload your entire vs code and then try again great so await Prisma db.usersubscription dot find unique like this where user ID like this so we're gonna find a subscription based on the currently logged in user ID uh if there is a user subscription already and if there is user subscription dot stripe customer ID in that case once user uh points to this API route we don't want to create a checkout page instead we want to redirect the user to the billing page so that they can cancel their active subscription in order to do that we're going to write const stripe session is equal to await stripe dot billing portal.sessions dot create like this and inside of this object let's give it a customer a user subscription dot stripe customer ID and return URL is going to be settings URL which we defined right here that's the absolute URL thing great so once the user finishes doing whatever they want on the billing portal they're going to be returned to localhost 3000 slash settings which for now is a404 in our project but don't worry we're gonna fix that later great so we have our stripe session and what we have to do still inside of this if block is actually returned that back so return new next response like this json.stringify like that URL stripe session dot URL like this perfect and now that we have that let's see what happens if we don't have a stripe subscription so if we don't have a stripe subscription we want to create another form of stripe session so go ahead and write con stripe session again but this time we're going to use a weightstripe.checkout.sessions DOT create so we're not going to create a billing portal but a checkout session because it's user's first time subscribing to our application we're going to give it a success URL to be settings URL but it's also going to be the cancel URL so we don't really care for our application it can be the same payment method types are going to be an array and I'm only going to accept credit card mode is going to be subscription like this billing address in collection is going to be Auto customer email is going to be user.email addresses so this is from clerk so this user object comes from clerk right here so uh clerk has an array of email addresses and Greek is just going to pick the first one like this dot email address like that and line underscore items is going to be an array inside with just one object so this is going to be our product and it's going to have price underscore data which is an object currency is going to be US Dollars product underscore data is going to be another object with the name of Genius Pro and description is going to be unlimited AI Generations like that perfect so we finished up uh price data uh actually we haven't so we finished that product data now we have to add the unit underscore amount which is going to be 2 000. so this is equivalent to 20 dollars and we're gonna add the recurring option because this is a subscription to be on a monthly interval like this great and lastly let's just give it a quantity of one so quantity is part of the main align items object not the price data object you can see price data is a separate object for itself so make sure you put the quantity outside of the price data object great and one more thing that we have to pass outside of line items so you can close line items I mean you don't have to close them but just outside of the array make sure that you add metadata this is actually very important user ID the reason metadata user ID is important is because this is not gonna create anything yet this is only gonna open the checkout page and only after users successfully purchases our monthly subscription are we gonna create a web hook which is going to catch that and then it's going to read the metadata find the user ID and then say oh okay so this checkout which was just completed successfully belongs to this user ID so it's very important that you pass that otherwise users will be able to check out and subscribe to your application but you won't know who actually subscribed and who to give subscription to so that's why this metadata is very important uh great so now we have that and we can now simply do a return new next response json.stringify like this URL stripe session dot URL like that perfect so you just finished the stripe checkout page great and what I want to do now is I want to create a web hook for our stripe so I'm gonna close everything here and we're gonna go back inside of the app folder inside of API and create a new folder called webhook like this and inside create a new file route.ds like that perfect so let's go ahead and let's import Stripe from stripe let's go ahead and import headers from next slash headers like that let's import next response from next slash server let's import Prisma DB from Prisma DB and let's import the stripe in util that we created from s slash lib stripe now let's export a synchronous function post which is going to take the request which is a type of request let's go ahead and let's get the body in a form of a weight request dot text like this and let's get the signature so const signature is equal to headers dot get stripe Dash signature as a string now let's create a let for event which is a type of stripe dot event like this and now we're going to open a try and catch block right here uh okay and let's go ahead and let's write event is equal to stripe.webhooks dot construct events we're gonna pass in the body we're gonna pass in the signature and we're gonna pass in the process.environment and you already know which one this stripe uh API key actually no my apologies actually this is uh this is going to be stripe underscore web hook underscore secret which we are going to create in a second so we don't have this yet but don't worry we're gonna create it my apologies I I thought it was the other one uh great and let's just add a catch here for an error of any and return new next response webhook error error.message and a status of 400 like this great and now that we have that let's create the logic which is going to capture the current so we are only going to look for two events the checkout session completed and invoice payment succeeded so two events because in our stripe route we only do two things we are either doing the billing portal or we are doing the checkout session so those are two separate events one is going to either cancel or upgrade the subscription and the other one is going to create it for the very first time so those are the two events that we are going to look for inside of our web hook so let's go ahead and let's write const session is equal to event.data.object as stripe.checkout.session like this great now let's check for the first event so if session so if events DOT type is equal sorry identical to checkout.session dot completed so make sure you don't misspell any of this in that case let's go ahead and retrieve the subscription using stripe silicon subscription is equal to await stripe dot subscriptions dot retrieve and just pass in session dot subscription as a string like this if there is no session question mark dot metadata question mark dot user ID so that's what I was talking about we have to pass that user ID in the metadata but if it's not here we don't know who this subscription belongs to in that case we have to throw an error return new next response user ID is required like that and let's just pass in a proper status of 400 as well great if there is we can go ahead and actually create a Prisma subscription so away Prisma DB dot user subscription dot create like this and the data we are going to pass inside is going to be user ID which is going to be session question mark metadata question mark user ID so the same one we had right here so that's how we are going to match uh the currently logged in user using web code because in this web hook we cannot use clerk because this webhook is going to be running independently from our application right and we're also going to have to add this webhook to public routes as well uh great because it's going to be accessed by stripe in a different way great so that's how we get the current user ID without actually being logged in and now we have the stripe subscription ID which is subscription ID we have the stripe customer ID which is a subscription Dot customer as string we have the stripe price ID which is subscription DOT items.data first in Array because we only have one item Anyway dot price dot ID like that and stripe current period and is going to be new date which is going to take in subscription the current underscore period underscore end times 1000 like this perfect so we created a user account in case this event is run right here great so let's go outside of that if function and let's check for the second one and that is if user just upgraded their subscription and they already had one before maybe it has expired or something so if events.tight is identical to invoice.tayment underscore succeeded whoops like this in that case Hans subscription is equal to a weight stripe dot subscriptions dot retrieve like this session dot subscription as string again and we're simply going to update the existing user subscription so await Prisma DB dot users subscription dot update where stripe subscription ID is equal to subscription dot ID like this and data is going to be stripe price ID because we have a new price ID now because we have an entirely new subscription so subscription Dot items.data burstinaray.price.id and we are going to update the current period and because user just renewed their subscription so a new date and inside stripe sorry subscription dot current underscore period end times 1000 like this perfect and now that we have that all we have to do is just return new next response no and the status of 200 so this is actually quite important make sure that you return this it doesn't matter that we are not sending anything but it can mess with the web hooks functionality if you don't do it properly great so what we have to do now is we have to actually uh create this environment variable but there is a certain way we have to do it in local development what we have to do is we have to go back inside of stripe and you have to click on developers right here in the corner so make sure you click on this developers button go inside of your web hooks here and when you are in production you're going to click add on endpoint but we are local so we're going to click test in a local environment so first thing you have to do is download the CLI so you can click on download the CLI right here and you can see some options on how to do that so I'm on the Macbook so I can just use Brew if you run a Windows you can go ahead and download from GitHub and run stripe.exe and a bunch of other options from Linux Mac OS and a bunch of other tools you might use so make sure you have the stripe CLI setup and now you have to do the stripe login so I'm going to copy this to the clipboard I'm going to go ahead in my terminal and I'm going to open my third terminal actually because one runs my project one runs my Prisma so I'm gonna add a new one and this one is going to do stripe login there we go so my pairing code is this one and I have to click on this link right here and open it and there we go it's opened in my dashboard now here and you can see that I have the very same pairing code that is in my terminal so that way I know okay this is certainly mine and I can allow access to it there we go now I have my access granted and take a look at the uh take a look at my terminal you can see that the stripe CLI is configured right here uh yeah okay so everything is correct that's what I wanted to point out uh great now let's go back inside of the dashboard AI right here and what we have to do is we have to simulate listening to our web hook so copy the second one and you should see the completed green tag right here so go back inside of your terminal now copy this and you have to modify it a little bit so it's not localhost4242 instead is 3000 slash API slash web hook so the route which we just created log host 3000 slash API slash fabcook I just want to zoom out so you can see it in one line stripe listen dash dash forward-2 localhost 3000 slash API slash web hook press enter like this there we go you can see that it now says completed here as well and now we finally got our web hook signing secret so you can go ahead and copy this so starting with a whsec so go ahead and copy this entire thing after the last number make sure you don't accidentally copy some spaces so be very precise when copying this and then you can create this stripe underscore webhook Secret in your environment file so go ahead and add stripe underscore webhook underscore secret like this and just paste that in here just make sure there are no trailing spaces on either end great so stripe web hook secret and just double check in the web hook that you're actually using that one there we go and make sure to keep this running in your terminal every time you are developing otherwise your stripe web Hook is not going to work so for now make sure that your app is running make sure that your Prisma studio is running and make sure that you have the stripe listened forward to localhost 3000 slash API slash web hook running as well perfect so I'm gonna close this now and what we're gonna do now is we're going to actually make it so that when we click on this upgrade button it actually calls this route stripe which is going to either open the billing portal or open the checkout session so let's go ahead and let's close everything and let's see how we can do that now so let's head inside of our components inside of our pro model right here I'm just going to expand this and what we're going to add is we're going to add const on subscribe to Be an Arrow function and it's going to be asynchronous like this go ahead and open the try and catch block so you can just log the error here and let's just write stripe client error like this and let's go ahead and just do const response is equal to axios so make sure you import axios I'm going to move it to the top of my imports all the way up but always make sure your Imports are below use clients so that's also important great so we have the axis now so axios is going to be dot get slash API slash stripe because that is the route we created in our app folder API we have the stripe folder route DS and you can see that it is a get method so that's why we do axios.get slash API slash stripe and what are we gonna get in this response well you can take a look at that right here so we are going to get an URL inside of an object for both cases you can see URL at the bottom and URL in this case too perfect so let's go ahead and run window.location.href is going to be equal to response.data.url so I just realized that my vs code automatically added a weight response and I didn't mention anything about it so that's because I honestly didn't even notice this so here is a screenshot of an alternative way you can do this you can put a weight in front of axios.get and that is a preferred way of doing this like this but before we do that let's also add a loading state so loading set loading use state from react give it the default value of false and I'm just going to move this along uh with the other Global Imports like that and in the finally I'm going to add set loading to false and in the try I'm going to add set loading to True like that there we go and now let's just add this on subscribe to be an on click of this button like that now let's go ahead and I'm going to try and click upgrade but I'm just going to refresh first just in case so again make sure you have your Prisma Studio running so you can look if there are any active subscriptions here so here are my use here I'm looking for user subscriptions in the Prisma Studio nothing is here make sure that you have uh your and make sure that you have your web hook running so I'm just going to open my terminals quickly so make sure you have this terminal running right here uh make sure you added this stripe API webhook and now let's go ahead and let's see if this is going to work so I'm going to click upgrade right here and we forgot to append the loading states to it but there we go you can see that it uses my email that I logged in with you can see that it puts 20 dollars per month unlimited AI Generations now let's use the test credit card from stripe and I'm just going to expand this so you can also keep my uh so you can see my terminal so open your web hook terminal here so you can see the actions in real time here I'm going to add a fake stripe credit card like this I'm gonna add some more fake information I'm going to use my name and I'm going to click subscribe now let's see what happens let's see if we did everything correctly or if we did a mistake and we did a mistake we are getting a 401 error here and let's take a look at why that happened so now if you refresh your Prisma Studio I'm really confident you should see nothing here that's right so it didn't work the reason it didn't work is because we forgot one crucial thing so it tried to go to our localhost 3000 slash API slash web hook but we are giving it a 401 error meaning that we are not authenticated but how do we do that how do we fix that well very simply using clerk so you have to go inside of our middleware.ts right here and alongside public routes we also have to add slash API slash webhook like that and now our web Hook is public so let's repeat this process one more time so I'm just make sure you log in make sure you go to slash dashboard so manually have to go to slash dashboard go ahead and click on upgrade again and I'm gonna keep my terminal running again and hopefully this time we are not going to see 401 instead we're going to see 200 and we're going to see a new model in our Prisma studio so let's go ahead and write this I'm going to use my name let's click subscribe fingers crossed in a couple of seconds there we go all events are 200 and we have succeeded this is a 404 page because settings page does not exist yet but we're gonna create that now and let's see our Prisma studio if I refresh here there we go look at this my user id my customer subscription and my uh period so I can see if it has expired or not beautiful amazing amazing job great and now let's actually go ahead back to slash dashboard and let's create this settings page but before we do that let's create a useful util to check if our currently logged in user is subscribed or not so we can just check whether it has a subscription in the database we also have to confirm that it has not expired right so we're going to create a util for that go inside your lib and create a new file called subscription.ds like this let's go ahead and import out from clerk again and let's import Prisma DB from Prisma DB or dot slash Prisma DB depending on how you prefer it let's write a constant for an entire day in milliseconds so const day in milliseconds is equal to 86 underscore 400 underscore zero zero zero like this great now let's write export const check subscription is asynchronous Arrow function like this let's extract the user ID from out like that if there is no user ID in that case return false meaning we have no way of checking whether user is subscribed or not so we are just going to assume that they are not next we're going to try and find the user subscription so const user subscription is equal to await Prisma DB dot usersubscription dot find unique I'm just going to expand this so you can see a bit more like this where user ID is equal to user ID or you can use the shorthand and we're going to select the following we're going to select stripe subscription ID to be true we're going to select stripe current period and to be true we're going to select stripe customer ID to be true and we're going to select stripe price ID to be true as well now let's check if we actually got that user subscription so if there is no active user subscription in that case we can also return false because obviously this user is not subscribed to Pro and now let's check if it has a subscription but let's check if it's actually valid and not expired so const is valid is going to be user dot user user subscription dot stripe priceid and user subscription dot stripe current period and question mark dot get time plus they underscore in milliseconds like this is larger sorry greater than date dot now like this so I'm going to expand it even more and to fix this error you can just go ahead and put an exclamation point right here so basically what we are doing here is we are checking if the current subscription period End plus one uh why we are giving it one uh day of grace period is larger than the current date and that's the way we check if it has expired or not and we're just gonna return question mark question mark is valid so please make sure you don't accidentally do this it's not the same thing I'm using question mark question mark to ensure that this is valid is always a Boolean like this there we go so we now have that and now we can safely create our settings page so let's go ahead I'm going to close everything I'm going to go inside of my app folder I'm going to inside the dashboard routes and I'm going to create a new folder called settings like this inside of settings I'm going to create a page.dsx and let's go ahead and write settings page like that and let's return a div like this and let's add a heading component from s slash components heading like that let's give it a title of settings let's give it a description of manage account settings like that let's give it an icon of settings from Lucid react let's give it an iPhone color of text Gray Dash 700 and let's give it the BG color of BG Dash gray Dash 700 10 like this and I'm just going to separate this Imports and let's actually try and go to our settings now there we go so we are on the slash settings and it's showing this exact thing that we are calling right now great so let's stay here and below the heading let's create a div with a class name of bx-4 a large is going to be px-8 space Dash y-4 like this let's create a div with a class name of text Dash muted Dash foreground and tax Dash SM and inside we are either going to say you're currently subscribed to a Pro Plan or you're currently on a free plan so what is the conditional to use that well we're going to use the udle we just created so let's go ahead since this is a server component we can just write const is pro to be await check subscription from s slash lib subscription and make sure to mark this as a synchronous function there we go so now we have the is pro right here and we can use conditional now so is Pro in that case you are currently on a Pro Plan otherwise you are currently on a free plan like this there we go you can see for me it says that I'm currently on a Pro Plan because I just did a success I just did a successful subscription and you can see that my subscription is in the prisoner studio and since I just did it it's certainly not expired perfect so it works exactly as we expect it to work and what we want to do now is we want to create a button to show uh billing inside right so let's go ahead and let's do that so in order to do that we're gonna have to create another component called the subscription button let's go ahead I'm going to close everything going to components and create a new file subscription underscore button uh sorry Dash button.ensx like this let's go ahead and Export cons subscription button like this and let's write an interface for it so interface subscription button props is going to take is pro option which is a Boolean like this great and I did a mistake here so it should be like this sorry and let's extract is pro here let's give it a default value of false and let's assign the props subscription button props like that great and let's just immediately mark this as in use client like this and let's go ahead and let's return a button component from dot slash UI button or you can rename it to components UI button like this and inside I'm going to use the is pro to either say manage subscription or upgrade so depending on VR subscriber or not it's going to be different when user visits their subs their settings page and here we're gonna do the opposite so if it's not pro in that case we're going to add the little zap icon we already did a couple of times from Lucid react like this and let's go ahead and give it a class name of w-4h-4 margin left to and fill Dash white like this and now for the variant we're also going to use the is pro condition here so if we are pro we're going to use default otherwise we're going to use premium great and let's go ahead and add on click to be on click which we have to create right now so let's go ahead and write const on click it's going to be an asynchronous Arrow function like this uh let's open a try and cache block so error console log Billings error like this inside of here let's go ahead and write const response is going to be again a weight access dot get slash API slash stripe like this so the same place and make sure you import the axis so the same place we are doing in the pro model but what is the difference well there isn't a difference but our API our route for our stripe is very smart so it knows whether we actually have a subscription or don't so if we have a subscription in that case we are going to open the billing page otherwise we are going to open the checkout page so you don't have to do anything different on the client which is great and then let's just do window.location.hrefresponse.data.url right here and let's just quickly add the loading States a const loading set loading is going to be your state false make sure you uh import this from react of course great let's set the loading the true in the try Block in the finally let's do set loading to false like this and let's add the disabled prop to be loading here as well great so now we have this subscription button meaning that we can go back inside of our settings page and just above below this div holding our text let's render that subscription button from add slash components subscription button right here and let's pass in the is pro to be is pro like this there we go so you can see that now it says manage subscription and if I click on it let's see if it's gonna take me to the checkout page I think we're actually going to get an error yes so we're gonna get an error if I look at my console there we go billing error in my console uh what we forgot to do and a very easy way to fix it is to go inside of your terminal right here uh inside of here and there we go I think this is we're gonna see uh your error so I'm going to zoom out so you can read a bit better it's going to tell you exactly what went wrong and it says you can't create a portal session in test mode until you save your customer portal settings in test mode so if you can't find this link I you can just click on this link and fix it but if for some reason you can't find this in your console let's go to stripe together and try to find that right here so I'm going to close this right here and let's see so what does it say uh I'm gonna scroll a bit down so it mentioned the customer portal right so let's see how we can search that customer portal okay so settings billing customer portal right here test mode and you have to click this activate link button right here so you can either click on that link from your terminal which is much faster but it's for some reason you can't find that in your terminal this is how you do it and now let's refresh this and let's try the very same thing again I'm fairly confident that this time there we go it's loading and we are redirected uh back to billing so let's just wait a couple of seconds to see how that looks there we go perfect you can see when it expires and absolutely everything great so let's go ahead and let's return back to AI SAS tutorial uh perfect great great job so before we do that I just want to play around with more with this subscription and all it can do so currently when we click on manage subscription and get this loading State you can see that it says that it expires in 2055 right so let's go ahead and try and modify the values to see the one that matches so I'm going to go ahead and I'm going to remove my user subscription from Prisma I'm going to click delete this record right here so and now it's saving changes I'm going to refresh and there we go no user subscriptions now I'm going to refresh here again and this time when I click on here it's actually going to redirect me to the checkout page but before we do that let's go back inside of our web hook right here where we actually create the subscription right here and let's see if we can modify the period end that to match only one month and I just realized that I gave you completely wrong information we don't have to change anything so what I read was the expiration for the credit card not for the subscription my apologies but still while we're here let's test out this button and how it looks when we are not subscribed so make sure you have removed the subscription from Prisma uh one more thing if Prisma studio is not working for you the way you can remove everything from your database is by using the terminal so I'm going to go ahead and open another one and I'm going to write ntx Prisma migrate reset like this so this is going to clear the entire database and just confirm that with yes and one important thing after you do this make sure you do npx Prisma generate again and npx freesma DB push so very important that you do the npx Prisma DB push after that so this just cleared the entire database so now I'm also not going to have any Generations at all see so I just wanted to give you that information in case studio is not working for you for any reason great so let's test this to upgrade now right here so I'm gonna give it a fake credit card my name and let's click subscribe right here after a couple of seconds we're gonna go ahead and get redirected back to settings let's click on manage subscription this time and now we are here and there we go you can see that it's a month from now so right now it's July and we're gonna have to uh we're gonna have to renew in August great so make sure you have a subscription active make sure that oops I just clicked again whoop yes so make sure this says you're currently in a Pro Plan make sure in Prisma Studio that you have an active user subscription working and now we can go ahead and actually enable this uh these models right now so right now I have all three generations so I'm just gonna go ahead and fill them up so I'm going to use this to create my first a one out of five free Generations I'm gonna go back in my Prisma Studio I'm gonna go to user API limit I'm gonna find the one for my user and I'm going to increase the count to five so I simulate filling up the entire uh the entire uh three generations but still if I try something now I still get the uh premium model even though I'm obviously subscribed you can see that right here so let's slowly go ahead in each route uh and let's go ahead and fix these things so first thing I actually want to change is I no longer want to show this three generations if we are subscribed so let's do that first I think that's the easiest thing to change let's go ahead inside of our components inside of free counter right here and what we're going to do is we're going to accept another prompt here called is pro like this which is a Boolean like that is pro is going to be false by default and in here I'm just going to add if we are pro so it is pro return null as well and now what we have to do is we have to also add that to the sidebar so let's go inside of our sidebar component right here let's go ahead and add is pro Boolean prop right here let's go ahead and extract it here so it's Pro right here to be false like that and let's pass it here to the free counter is pro is pro like that and now we have to oh I forgot to put a comma okay and now let's go inside of our app folder dashboard layout.tsx and let's just add const is pro to be away check subscription like this from s slash lib subscription and pass in is pro to be is pro like this great so now if I extract this you can see that it no longer appears here because we are on Pro and what we have to do next is also do that on mobile so let's go inside of our components inside of navbar right here and let's add const is pro await check subscription from s slash lip subscription and let's pass this to mobile sidebar so it's Pro is pro like this let's go inside of the mobile sidebar component which is inside of your components folder right here let's go ahead and add its Pro Boolean right here let's extract is pro here and let's give it a value of false and let's give this a value of 0 while we are here and let's pass in the is row is pro right here there we go so now it's working on mobile sidebar as well great so we just fixed the UI for this and now what we have to do is we have to go through individual routes and fix the generation problem so still if I try since I'm in 5 out of 5 on my count I'm getting this pro model so let's go inside of our app folder inside of API inside of conversation route.ts and let's go ahead and resolve this so first thing I'm going to add besides this API limit is I'm going to add import check subscription from add slash lib subscription right here and here where I fetch my free trial I'm also going to check const is pro to be a weight check subscription like this and then I'm gonna add to this if Clause so if there is no free trial and if we are not bro only then do we throw the error and same thing goes for increasing the API limit so we only want to increase it if we are not pro so if question mark is pro sorry if exclamation point is pro like this and now if you go ahead and test your conversation so test and click generate you can see that we are officially on Pro meaning that we are not blocked from using the open AI model because we have an active subscription uh great and what we have to do now is we have to copy this in all of our routes so let's go ahead and let's do that so I'm going to go ahead and I'm going to copy these two lines right here so it's taking some time so let me just refresh this and see what's going on okay so what happened in my case is that the service randomly fell down so what I did was I shut down my application I ran npm run Dev again and I refreshed my page that's it I also confirmed that my usage has not been uh filled up great so now that we have that working uh you can see how it's no longer blocking us with the premium model we have to do that for the rest of the routes so go ahead and just copy this if clause and these two lines right here because we're going to need them in the rest so let's go ahead and do code as well so replace this with what with what we just copied import check subscription from s slash lib check subscription like we did right here so I'm just going to expand it great and now that we have this let's also wrap the this so if we are not pro in that case let's go ahead and just do this great so now our code should work as well now let's go inside of image let's go ahead and replace this free trial as well let's import check subscription from add slash lip subscription like that let's go ahead and wrap this so if we are not pro in that case go ahead and wrap this in this if Clause great let's do the same for music so I'm going to replace this paste it here import check subscription from add slash lib subscription like this great and here again only if we are not pro do we increase this API limit like this and last one uh let's do the video right here so again select the if clause and free trial paste it import check subscription from lib subscription like this and only increase the limit if we are on Pro so if we are not on Pro in that case no point in increasing this perfect now let's go ahead and let's check if this is working fine so conversation we confirm that that is working how about we try this so a picture of Swiss Alps for example let's check if that is working fine now so I'm just gonna wait a couple of seconds here there we go that is working fine I'm just gonna quickly try and Trigger the video generation for example clown Fair swimming around a coral reef and I'm just gonna wait a couple of seconds to confirm there is no model popping up okay we are not gonna wait for the whole video and same thing for uh the music so piano solo let's wait a couple of seconds great no model popping up meaning that it's working and same thing for code generation so simple toggle button using react hooks I'm just gonna wait a couple of seconds and there we go you have officially implemented authentication sorry a subscription in your project and you can go ahead and play around with that great great job so far what we have to do next is Implement crisp for customer support and wrap up with doing a landing page and finally deployment before we move on to adding crisp chat for customer conversation what I want to do first is I just want to add a loading state for my pro model so in order to do that I just quickly gonna go back inside of my components inside of pro model right here and you're going to notice that we're setting a loading state right here but we're not using it anywhere so I just want to add it to this button right here so disabled is going to be loading like this so I just wanted to do that so our loading state is not wasted uh great so if you want to you can remove your subscription and test the loading model again uh or well you can just leave it like this and move on now what I want to do is I want to add a some better error handling because right now if we get an error in any of this models or stripe or anything we actually don't get any toast notification about it it just stops generating so let's go ahead and let's go inside of our terminal so yeah what I have running here is my npm run Dev here I have the Prisma studio and here I have the stripe web hooks so make sure you have those running as well so I'm going to go ahead and I'm going to install a package called react hot toast so go ahead and run npm install react Dash hot Dash toast like this and after a couple of seconds go ahead actually yeah so I'm already running the application in this terminal but you can still just refresh uh great now let's go ahead and let's go inside of our components and let's create a new file called toaster Dash provider.tsx mark this as use client so let me just give you the full name uh so toaster Dash provider.dsx that's the full name of the component great and Export cons toaster provider it's going to be a very simple function which just Returns the toaster from react hot toast like this great and now that we have this what you can do is go inside of your app inside the layout.tsx and the same way you edit model provider just add the toast provider toaster provider like this there we go and import it from add slash components toaster provider great and now that you have that let's for example go inside of app folder inside of dashboard routes conversation page right here and here where we have our error you can add an else to this pro model on open so this is the on submit function right so go inside of here find this handling of 403 error and you can just add toast which you can import from react hot toast so I just added that right here and I'm gonna move it all the way to the top right here and you can just write those dot error something went wrong like that so now I'm going to purposely throw an error in here so in this try I'm going to write throw new error something like this and I'm gonna refresh and I'm gonna go inside of my conversation here and I'm gonna try and generate and there we go now we have a nice uh toast that something went wrong so that's just what I wanted to do in case anything in here goes wrong let's at least give our users a nice message here so let's do that for code as well so just find the on submit and just paste it here and import toast from react hot toast like this great let's go ahead and do the same thing for our image right here so just catch that right there and import toast from react hot toast again and the same thing from music toast like this and the same thing for video right here so there we go and don't forget to import toast here as well so in all of these places you need to have toast imported from react hot toast and make sure it's in the else section of this great and now that we have that let's also do that in our pro model right here so instead of just rendering this error right here let's just actually do the toast which you can import from react.to error something went wrong like this so make sure you imported the toast great and now that you have that let's also do it in the subscription button component here so instead of console logging the error there we go and of course import toast here as well great so now we have a nicer error handling in our project great so let's go ahead and let's set up crisp now so in order to do that the first thing we have to do obviously is we have to create an account for crisp so either go to crisp.chat or write crisp chat on Google and go ahead and click get started or log in and of course it's free so just finish this input I'm gonna pause the video and show you the dashboard so in this step what you can do is just name this genius and leave this to be www.dummy.com so it doesn't matter for now later you can add it to your actual uh production website so I just wanted to clear that up great so now that you get this message right here the only thing that matters for you really is this code right here so you can technically just add this script but since we are working with react we actually have a package for this so let's go ahead and let's install a package and stay on this screen right here so I'm going to go ahead and I'm going to install ambient install crisp SDK web like this wait a couple of seconds and great and now let's go ahead inside of our components folder and create a new folder sorry create a new file inside called crisp.chat.dsx like this go ahead and Mark this as use client go ahead and import use effect from react go ahead and import crisp from crisp SDK web like this and Export const press chat like this and inside views effect you're gonna go ahead whoops add an empty array here and just run crisp.configure like this and inside you're going to paste this crisp website ID right here so let me zoom in to show you exactly what you have to copy so you have window dot crisp website ID and copy what is inside of this strings right here so you can copy the entire string if you want to so all the way to here like this there we go and then just paste it inside like this so make sure you don't accidentally have double quotes So if you have it like this this is wrong right you have to have it like this there we go and just paste that great so I'm going to zoom out now whoops I'm going to zoom out here uh and let's go ahead and just return null here great and now that you have uh that what you have to do is create a crisp provider so go inside components new file crisp provider.tsx so let me just expand this so you can see uh the full component name so crisp Dash provider like this Mark this as use client again and just write the import risk chat from dot slash crisp chat or slash components crisp chat and Export cons crisp provider like this return crisp chat like this great and now that you have that all you have to do is add the crisp provider to your app layout so go inside app layout right here and go ahead and paste it above the body so crisp provider like this and import it the same way you did with toaster provider and model provider great and I think that you can now click on Discover my dashboard here uh okay I'm gonna extend this and there we go you can already see in my lower left corner that I have a crisp icon here so let's test this out so yeah we have some messages here let's read that and let's go back in our inbox and now I'm gonna go ahead and write something hello I have a problem with image generation like that and you can see how it immediately goes back here perfect great great job so you successfully implemented Clerk and you can of course uh respond to your user so what are you having problems with like this and you can see uh how that is immediately reflected here great great job you successfully implemented uh Advanced error handling as well as full customer support let's go ahead and let's create the last part before deployment which is the landing page so first of all go ahead and sign out so you are redirected back to slash or just go to localhost 3000 without any routes right here so you're on this landing page which we are finally now uh gonna go ahead and make it look like something so go inside of your app folder and go inside of this Landing folder right here and go ahead and create a new file called layout.tsx like this go ahead and create a landing layout component like this the props is going to accept is children so go ahead and destructure children like this and immediately go ahead and give it a type of children react dot react node like this and inside go ahead and create a main element like this and give it a class name of H dash full BG Dash hex code 111827 like this and overflow Dash Auto like this and inside go ahead and create another div with a class name of MX Auto like this Max W screen Dash XL H dash full NW Dash full like this and inside you're going to go ahead and render the children like this great now let's go ahead inside of our page.tsx here and we can actually remove everything in here create a new div with a class name of h-4 like this and you can remove these imports from here like that and the first let's end the landing nav bar like this component so of course if you save you're gonna get an error so let's go inside of our components folder and go ahead and create a new file called Landing Dash navbar.tsx like this go ahead and Mark this as use client like that go ahead and import Montserrat from next slash font slash Google go ahead and import image from next slash image import link from next slash link import use out from clerk like this import CN probably utils and import button component from dot slash UI button or slash components UI button like this let's add our font constant so font is Montserrat like this and inside of its object right away 3 600 and subsets to be Latin like this great now let's export const Landing navbar like this it's an arrow function and let's go ahead and extract the is signed in from use out like this so why are we using use out here from Clerk and everywhere else if you remember if you search through this import we mostly used Alf as you can see here for example we used out here so what's the difference well use out is one of reacts code available in client components so that's why I really like clerk because they support both client components and server components so you don't have to pick one or another so that's why in here we have to use a hook right if we want to use out that means we are in server components great so now let's go ahead and return let's return another element like this let's give it a class name of p-4 BG Dash transparent Flex item slash Center and justify Dash between like that and before we go any further with this let's go back inside of our landing page and just import The Landing navbar from s slash components Landing navbar like this so we don't have this error and see we can actually see what we are developing great now inside here let's go ahead and add this link component which we imported let's go ahead and let's give it an href property to go uh to a slash like this and a class name of flex item slash Center like that and inside let's go ahead and create a div with a class name of relative h-8w-8 and margin right of four and inside we're going to render an image give it a fill property give it an out of logo and give it a source property of Slash logo dot PNG like this great and now just outside of this div which is wrapping our image go ahead and create an H1 element genius like this and go ahead and give it a class name which is going to be dynamic so we are going to use CN text Dash to excel font Dash bold text Dash white and then add a comma and write font Dash class name like this there we go so now we use the Montserrat font beautiful so I'm just gonna expand it so you can see how it looks in one line beautiful great besides this let's go ahead outside of this link component and create a div and let's create a class name Flex items Dash Center Gap Dash X-2 like that and inside create another link component like this like this with an href which is going to be dynamic dependent on whether we are logged in or not so if if we are logged in in that case we're going to redirect to slash dashboard otherwise we're going to redirect the slash sign up like this great and now inside of this link component create a button like this get started like that and let's go ahead and give it a variant of outline like this and let's give it the class name of rounded Dash full like this beautiful so you successfully finished uh The Landing nav bar now what we have to do is create a landing hero so another component Landing a hero like this so same thing let's go inside of our components and create a new file called Landing Dash hero.dsx like this let's go ahead and Export comes Landing the hero here I'm just gonna write their native saying hero and I'm gonna go back inside of my landing page here and just import Landing hero so we don't have an error and so we can see what we are developing so let's go back inside of the landing hero and same thing here let's go ahead and let's use is assign in from use out from clerk like that and now let's go ahead and let's add a class name to this div so class name is going to be text Dash White py-36 text Dash Center and space Dash y-5 like this and yes we have to mark this as client component of course we are using a hook here great now inside of here create a new div with a class name of text-4 Excel for mobile devices on small it's going to be tax 5xl on medium is going to be tax 6xl on large it's going to be tax dash 7 Excel like this give it a space slash y-5 and font extra mold like this and inside uh let's go ahead and write H1 element the best a i 2 or like this and you can see how the more I expand my screen the larger this text can become and the more I Collapse it the smaller it's going to be great and now let's go ahead and install a package called typewriter effect so I'm going to go ahead and run npm install type writer Dash effect like this and I'm going to press enter there we go so just refresh your page and now just below this let's create another div with the class name of text Dash transparent BG Dash clip Dash text BG dash gradient-2 dash R so to write like this after that from Dash purple Dash 400 to dash pink Dash 600 so we're going to give a gradient to our text inside and inside write the typewriter component like this from typewriter effect so import type fighter component from Dash typewriter effect like this it's a self-closing tag and go ahead and give it some options like this strings and we're going to write chat bot with the dot and copy this a couple of times so the second one is going to be photo generation third one is going to be music generation this one is going to be code generation and this one is going to be video generation like this great and now let's also give it an option of auto start true and loop through as well great and now you can see how it seems like this is a typewriting effect beautiful and it changes with all of the features that we have in our application great so really neat component here uh great now just go ahead outside of this div right here create a new div right here with a class name of text SM on small devices but the medium is going to be text Excel font dash light and text sync 400 like this and go ahead and write create content using AI 10 times faster like this there we go and let's go ahead uh and let's try and expand this so you can see how it looks great uh and now below that go ahead and create a new div and then add a link inside give this an href it's going to be dynamic so if we are signed in again it's going to be slash dashboard otherwise it's going to be slash sign Dash up like this and make sure you import the link component not from Lucid react from next slash link like this great so we have that now and let's add a button component from dot slash UI button or components UI button like this great and inside of this button let's write start generating for for free like this and let's give the variant of Premium like this and let's give it a class name of MD tax Dash LG e-4 mdp-6 like this rounded Dash full and font large semi bulb like this so we create a bit of a custom style depending uh on the screen size so here is the Fuller version there we go you can see how nicely it increases depending on our screen size uh great now that we have our button finished I'll just let's create a new div with a class name of text sync-400 text that excess and medium is going to be textlash SM and font Dash normal like this uh no credit card required like this because we have a free tier so users can test this out for free uh great so you successfully finished the hero component now all that's left is the landing component so let's go back inside of our layout page right here and let's add a new component chord uh Landing content like this and same thing let's go inside of components create a file called Landing Dash content.tsx export const Landing content like this and let's just return it div same content let's go back inside of our landing page and import Landing content like this from s slash components Landing content so this error goes away great now let's go ahead and let's create a class name here so class name is going to be px-10 pb-20 like that remove this add an H2 element same testimonials like this and a class name of text Center text-4 Excel text Dash white font Dash extra bold and margin bottom of 10 like this great and now let's create a new div which is going to be our grid so grid grid grid Dash calls dash one on small devices SM grid Dash three LG grid Dash course Dash 4 and GAP 4 like this great and now let's go ahead and create an array of our testimonials at the top of this page so we can also Mark this as use client as well and let's write const testimonials like this which is an array we're just going to have objects so name I don't know Antonio Avatar is going to be a like this title is going to be software engineer and description is going to be this is the best application I've used or pretty much whatever you want inside great and now that we have one element inside let's actually go ahead and render that immediately so you can see what we're working with so testimonials dot map uh item like that let's go ahead and return a card so import card from dot slash UI card or from slash components like this let's go ahead and give it a key which is going to be item.description and the class name is gonna be BG Dash uh hash 192339 like this border Dash none and tax Dash white like this great and inside add a card header again from s slash components uh UI card like this great let's go ahead and give this uh a card title in inside so make sure you imported car title as well great and give this a class name like this of Plex item slash Center ngap-x slash two like this right so you can see a little component right here uh great and inside of this card title open a div open a paragraph and write item dot name like this and copy that and write item dot title like this great and now let's give it some Styles so right here I'm gonna write class name text LG like this and here class name is going to be tax Dash sync Dash 400 and text last at the text Dash SM like this great so that's it for card title and now let's write card content like that inside and we're going to render item.description inside great and give it a class name of pt-4 npx-0 like this there we go and we can go ahead and copy this as many times as you want so I'm going to copy it four times and there we go you have your testimonials page and you can go ahead and edit this the way you like perfect it's a great great job you finished the entire software as a service with landing page stripe authentication five different AI models customer support you've done an amazing and amazing job all that's left is to learn how to deploy this application so let's go ahead and let's deploy our application so first thing we have to do is we have to go inside of our package.json right here and we have to add a post install script to be Prisma generate like this great now I'm gonna go inside of my terminal and you can finally shut down all of this web Hooks and Prisma Studios and whatnot so I'm going to shut down all of this and I'm gonna only live uh one open like this great now what I want you to run is npm run lint so go ahead and check if there are any errors in your project so I don't have any errors so great what I'm going to do next is I'm going to go inside GitHub and I'm gonna go ahead and create a new Repository I'm going to name this repository AI SAS tutorial like this it's going to be private for me and I'm going to click create repository right here and since this is an existing repository I'm going to use the second option so push an existing repository from the command line I'm going to copy all of this and I'm going to paste it here but actually before I do that ignore this go ahead and give it a git add get commit and just add initial commit like this so everything is committed great and then paste this and push to master like this and once I refresh you can see that my project is right here along with all the code great now we can go ahead in our reversal and you can go ahead I'm just going to refresh go ahead and click add new project right here and go ahead and select this AI SAS tutorial which we just created right here perfect what we have to do now is we have to add all of our environment variables here and we're going to have to change them once we deploy so let's go ahead and let's go inside of our DOT environment file and you can just go ahead and select absolutely everything and just paste it inside like this and then just go ahead and click deploy and of course you can see that next public app URL is localhost so don't worry about that we're gonna have to change that but first we need to see if our deployment is going to pass so I'm going to pause this and see if there are any errors that I get and there we go my application is successfully deployed but now we have to change a couple of things so let's see what that is I'm gonna click continue to dashboard right here and I'm gonna go ahead and get my URL so you need to have your url so right here for me it's AI Dash SAS the dash tutorial.versal.app so what I have to do first is I have to go inside uh uh let's first go into stripe so stripe.com right here and we have to change uh this web hook right so let's go ahead let's go inside of web hooks if there are no results just click on developers again and go inside web hooks here and we have a local listener here right here but that's local so go ahead and click add endpoints paste your new URL slash API slash web hook like this so webhook only one like this and now you have to select the events that we are listening to so what are those events well let's take a look in the web hook route right here so first event we need is checkout.session completed so let's go ahead and find that so we have the checkout option here and let's select the checkout.session dot completed like that and the second one we are looking for is invoice dot payment succeeded so let's go ahead and find that as well so all the way down we have an invoice and let's find where it is invoice dot payment succeeded and click add events and add endpoint like this and there we go now you have your sign-in secret right here so go ahead and click reveal here and copy this secret and now go back inside of your project settings here so I clicked on settings in this project go ahead and click on environment variables and let's go ahead and find that webhook secret so it's last one for me stripe webhook secret let's go ahead and click edit right here and just replace it with the new one and click save make sure you copy the sign in secret and not whatever this is so it needs to be designing secret great and what we have to change next is the next public app URL and that is going to be uh this very URL that we are using so let me see what this is so https AI says tutorialversal dot app like this without the trailing slash like this so edit that and paste it here just make sure there are no spaces like that and save the file great and after you change that go in your deployments and click redeploy again make sure you don't accidentally check this so just click replay like this so I'm gonna pause the video again and I'm going to show you what it looks like what's once it's finished great so it's finished and one thing I'm gonna do uh before I visit the website is I'm just going to reset my entire database and I'm going to push it again so inside my terminal I'm going to write npx Charisma migrate reset like this this is going to remove the entire database so be careful uh and after this is done npx Prisma DB push again and after a couple of seconds I'm gonna go ahead and visit my website so we're gonna visit this right now and there we go it's deployed right here and you can go ahead and click get started for example I'm going to use Google right here I'm going to log in with my account and you can see that I have my three generations right here so let's go ahead and test it out now uh for example what is the radius of the sun right here there we go let's ask it what is the radius of the Earth right here let's try of Mars so I'm just using this up to fill up the questions uh what is the earth of the I don't know penis like this it really knows everything huh and Neptune great now we use five out of five so I'm gonna add another one now we get a prompt so let's see if this is going to work okay I'm gonna go ahead and add a fake credit card here I'm gonna click subscribe right here and let's see if that is going to work or if we're gonna get uh any errors here uh okay so I'm logged out I think that's something with the configuration but let me log in again to see if yeah okay so we are on a Pro Plan great uh I'm not sure why we logged out I think it might be I think it might be because of the redirection stuff or maybe some hydration error that triggered the log out I'm not entirely sure that's something I'll have to uh look into and I'm not sure it happens every time because for me it worked normally in production great so this works now and just one thing you want to bring your attention to so there are stuff inside that are going to take too long to generate for virtual hobby right for example if I do Swiss Alps like this I think this is still going to work because it's not a very uh complex model so open AI Works faster so this works but for example if I try music generation like piano solo uh it might take so long that it actually throws an error so that is because you are we just deployed Universal hobby plan right so we're using versal uh for free right now so what you can do you can see it says hobby right here uh so what you can actually do uh is either upgrade to a newer plan so you see for me it works but it there's a chance that you get an error you can either upgrade to a larger plan and then you can increase how much uh what is the timeout for your API routes or you can go ahead and search for reversal AI SDK which is something I'm going to work with in my next tutorial so versatile AI SDK works on the edge Network which is also something you can google and find out more about it's basically has a larger timeout and is generally faster and it allow it supports streaming of content so that's what we are going to play around with uh in my next tutorial so thank you so much for watching this tutorial you did an amazing amazing job uh remember to leave a like share and subscribe if you like this content and see you in the next video
Info
Channel: Code With Antonio
Views: 160,469
Rating: undefined out of 5
Keywords:
Id: ffJ38dBzrlY
Channel Id: undefined
Length: 333min 29sec (20009 seconds)
Published: Tue Jul 18 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.