Build & Deploy: Full Stack AI Course Generator SaaS with NextJS 13, OpenAI, Stripe, TailwindCSS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's Poppin I'm Elliot a web developer from Singapore and today we're going to show you how you're gonna build this AI powered cost generation platform so here we can see that it's hosted on a custom domain and also these are basically courses that AI generated so these are all beautifully start with chat CN and towing CSS offering a light and dark team so let's first sign up so we are able to implement a Google sign in using Nexus so we'll be able to sign in without Google account to this platform so after we have signed in we are able to create courses so let's create a course by clicking on the create course button and here we can see that right now we have 8 of 10 free Generations so today we'll be also integrating the stripe API so that you are able to upgrade to a premium tier such that you're able to get unlimited generations of the course so this is actually a real SAS product software as a service so you can actually use this stripe integration to actually start making money with this platform so let's just demonstrate the course donation so for the title of the course this is a very big overview so I'll go enter calculus so this is going to be the umbrella like the big course title that you will need to generate and you can have multiple units sub units related to the big chords so in this case I want to learn about different Association and integration so I'm going to leave it at two remove the third unit angle press let's go so with this it's going to actually hit the backend API using next year's 13.4 new app directory and then with that with that you will actually use open AIS API to then create the courses and the units and the chapters such that we're able to then use the YouTube API to actually get the videos out so let's wait for a while as it changes to questions okay so we're back and basically look after he has generated the question we can see that it has come down here and has offered us uh with the costume of calculus and it has generated multiple subunits based on what we have entered so it talks about limits and continuity derivatives integrals differential equations and applications so it has generated a total of uh five units and each unit has a multiple chapters so that's chapter one chapter two for each of the units so with all that this is the magic part I'm going to click on generate and Watch What Happens so we're gonna basically uh for each of the chapters it's gonna hit the YouTube API and try to get the transcript from the API and with that transcript it can actually generate a concept checks or like questions so that we're able to practice our skills and learn so you can see that it is using like a is using asynchronicity so each chapter is gonna go out and hit the end point by itself and then when it's done it will turn green indicating that it has finished generating the chapter itself so we'll wait for a while yeah so you can just see that uh as it is going to hit the endpoint each of them is slowly coming back as green so after all this has been green then we can actually view the chords that has been generated for us so we're just waiting for the last one uh chapter two of unit two a derivative rules I'm gonna zoom in a little so we're just waiting for it to come back so sometimes it may be stuck but it will come back soon enough okay and we're back so now we can see that all of them has turned green and with that we can press save and continue and it actually bring us to the course that he has generated okay so we are here so we can see that this is a beautiful uh list out uh platform so we're able to see all the units we have here okay I'm not here to click on next to go to the next chapter of the previous chapters and we can this is the the relevant YouTube video that has been pulled up based on the chapters title in the summary so we can actually come down here and just view all uh view the different chapters that he has generated so we can see that ISO uh generated a concept check so why is the differential equation used for so we have a source options and basically it will uh tell us whether we are correct or wrong so these are all these questions actually generated from the transcript of the video itself so we're gonna be feeding in the transcript of the YouTube video into the AI the AI will then generate relevant questions uh for the chapter so we can see that the summary this summary is the summary of the the transcript of the YouTube video so these are all be linked nicely using a lot of prompt and engineering and also a lot of AI applications so we'll be learning a lot of things today so be learning authentication using next off then we'll be building on the front end with chat CN and turning CSS then for the back end we'll be using a several good apis like to open AI API the YouTube API and also the unsplash API so this unsplash API will just help us get the images so if you go back to the the gallery page of the courses here we can see that all the images that is actually being rendered here all comes from the unsplash API so we actually search up for the relevant image based on the course name and of course we'll be learning how to integrate stripe so that we can have a payment service and the last few things is actually the most interesting things is I'll teach you how to deploy to a VPS a virtual private server and we're using a digital ocean for this exercise so digitalocean provides us with a basically virtual private servers so these are basically computers that's holded hosted somewhere else in the cloud and you're able to rant them and then you're able to run your applications on this virtual private servers and then I'll also be teaching you how to use GitHub actions for proper CI CD upon code push and then lastly I'll teach you how to link up your domain name with a SSL set to VPS so that we can see that right here we have a https and also we have the custom domain name so all of these will be really interesting to learn because usually for next year's applications you're taught to host it on yourself right but today I won't be doing that because versal actually has a limitation when we go to versaille and Jose and Versa Versa only limits your function execution time to 5 seconds so your back-end endpoints can only have a maximum of five five seconds to execute and we can see that actually just now when we are generating the courses it's actually taking quite a long time because we are hitting a lot of AI endpoints and this AI endpoints actually take more than five seconds to generate the courses so we are actually limited by the free T of versaille therefore I want to teach you how we can actually uh host your self-host your self-host your next GS application such that you can able to run the functions for as long as you want without it timing out and of course let us actually demonstrate the strap integration so under the settings page right now I can see that I'm a free user if I come down to the create course we can also see that I have 7 out of 10 free Generations so just now it was at 8 out of 10 and because I generated one quality it has dropped back uh dropped by one so I can actually press upgrade to upgrade to unlimited uh feed here and it'll bring me to the strike page so we'll pay 20 and then because these are this is in test mode I can just enter the test a credit card okay uh let's enter the test credit card and then I'm gonna enter my name here and so subscribe so we'll be integrating the stripe API so after you're subscribe it's gonna hit a web hook in our next yes application then it will basically create a new role in the user subscription so that now we can see that we are a pro user and if you go down to create course we can now see that there's no longer the limit here because we are we are we paid for the premium tier and therefore we get to create unlimited courses so we'll be learning really a lot today and so uh it's a very different type of tutorial whereby you're just doing the front end the back end and just hosting it on yourself and that's all so we'll be learning a lot of different apis today so I hope that you are ready so uh some prerequisites that you need to do before having this project is you might need a domain name right so uh you can either get domain name from let's say Google domains or something like GoDaddy so you can get a domain name for pretty cheap but I'm gonna assume that you have a domain name and you're able to log into the dashboard so in this case I own the domain name relevit.tech right so I'll be using that domain to actually host my application so with that let's actually start building the application so the first step is let's actually initialize the new next year's project so what we'll do here is I'm going to enter the command uh npx creates next app at latest using the typescript flag so this will create a new next year's project for us so I'm going to press enter and we'll have to download the latest version so we're using 13.4.19 and what's the project name this I'm gonna name it a learning Dash Journey Dash YouTube okay so we'll be using yes lint uh telling CSS I will be using the source directory and this is the new Nexus 13.4 app router you could click yes I will leave the default for everything and we'll wait for it to actually initialize and download the dependencies okay so now that we've initialized the project let us seating Into The Learning Journey Dash Dash YouTube and let's open up in obvious code with the code dot command okay so we have this uh vs code instance up here running that's good so the first thing I'm gonna try to do here is let's zoom in and let's do npm uh run Dev to make sure that everything's working on the server is up so I'm gonna basically code it in this way move it to the left and open my browser on the right so that we're able to see it side by side okay so I'm gonna navigate to localhost uh 3000 and we're gonna see the project up and running okay so we can see that yes it is running so we have this uh app page running and basically we can see that this is the source app page.tsx file so I'm gonna come into the source slash app slash uh page.tsx and so this is how the new app router is structured so if you come down to the directory we can see that there's a source folder and inside that we have app directory so the page of TSX is actually a special file name such that whatever page here it actually Maps it to the roots so even the Korean groups like slash quiz I have to come down to the app I can create a new uh create a new folder called uh quiz and then under the quiz folder I create a new file called page.tsx so this is a special name therefore I can then do tsrafce which stands for typescript react Arrow function export component so it's one of my Snippets I'm gonna just do quiz page I'm Gonna Save it and then if I come down to localhost a slash quiz I'll be able to see whatever I have in this page in this case yes we see the quiz page here corresponds to this quiz page so that's just how the app directory routing works so I'm going to delete this file we're gonna be building up the rest of things together first so first I'm gonna just uh Delete the everything on this page right I'm going to restore reset return a normal hit run with the class name of let's say text Dash rate that's 600 with a name of hello world you can save the file you can see that okay it's good it's working so we can know that telling is working and that's perfect okay so now let's uh set up CN so what I said for those who are unfamiliar so CN is a component Library built on Radix UI and towing CSS so you can see that uh it's basically a component library but instead of downloading packages we actually have to copy over the code so how it works is let's say we go to the button component we have this chat CM button so how do we actually use it to use it we have to do the installation so there's two ways of installing action component either manually that means we actually install the Radix UI components and copy paste the button code into our project or we can use the command line interface which is what we're gonna do so what I'm gonna do first is I'm gonna come down to our vs code instance I'm going to open the terminal I'll split the terminal so that we're able to see it okay and the first thing we're gonna run is uh let me just expand this MPX chat CN Dash UI at latest in it so we're going to be initializing the chat CN configurations within this project so we'll be using typescript yes uh we're gonna leave it as the default Style uh the the base color can be just anything I'm gonna leave it as late and it's asking us where's our Global CSS file so if you come down to our directory we can see that we have the globals.css file in slash app uh Source slash app slash globals or css what I'm going to do is I'm going to come down here and delete the file and then I'm going to come down here and tell it it's gonna be in Source slash app slash globals.css so we are letting chat CN handle the global CSS file therefore we'll just leave it like this so by using CSS variables or colors plus yes and where's our Tailwind of config.js we can see that it's in this uh base directory so we'll just leave it at that and we'll leave the import areas for components just sd4 here Levy as default also so basing react server components because we're using next year's r13 so press yes and it's asking us if you want to write the configuration to components.json in this case we want to so press yes okay so awesome we have set up sets yet so you can see that he has actually written a components.json to our root root file and this basically contains all the set saying configuration that we have just entered so let's actually try rendering out the button so whenever we want to install or use a new component in session we have to manually install it by doing MPX chat CN Dash UI at latest add button right so this is the same command that is running here so we're gonna install the dependencies and let the code be copied over so as it's installing we can see that if you go down to Source under components right we can see that there's a new UI folder and in this UI folder it says button.tsx so this is the actual uh chat CN button code right we don't have to actually Touch Wireless in here we just need to know that it exists and we can import it so I'm gonna close that under this page of TSX instead of just rendering a hello world here I'm going to try to render a button by CN so I'm going to remove that I'm going to return just a button so this button will be imported from uh component slash UI slash button I'm going to press enter for it to import and I'm gonna say hello world exclamation mark and so there we can see that the chat CN button is looks beautiful and it works so that's good we have actually set up chat CN okay perfect so after we shut up session let's now actually set up the Prisma Planet scale database so we're using Planet skill so Planet scale is um Cloud hosted manage a mySQL database so with Planet scale you're able to create a free database and we're able to use the MySQL for this project so if you come down to the planet scale website and go to dashboard so in this case I have it open already here so you just create a new organization under your name and then you can come here and press create new database so let's just name it learning Dash uh Journey Dash YouTube and choose the region that's closest to you in this case I'll choose Singapore and we'll choose the hobby here so this is gonna be free right and if you scroll down here it's gonna say that it's only available first Killer Pro and then under here uh it says we have to add a new card yeah so you actually have to add a credit card so this won't be charged because this is all free it's just that you need to add the debit card so they will prevent fraud so people don't actually spam the databases so just kind of add a new card okay so after you have entered the details you can just press create database and you'll actually create a new database for you so let's wait for that so while we're waiting let's set up Prisma so Prisma is a object relational mapper orm that basically wraps the SQL SQL language so that we're able to interact with the database with more type safety so well I'm gonna come down here and we're going to install a field package so the first package we're gonna install is the actual Prisma thing so npm install right Prisma that's a safe depth so this will be a developer development dependency okay the next thing we also want is the npn install at Prisma slash Clan so this Prisma slash client is the actual client that will be interacting with the database okay so I'm also gonna basically come down here so we're gonna create a new uh Prisma initialization so I'll do MPX Prisma in it right and then pass in the data source data source provider flag and we'll pass in MySQL so with this configuration you'll create we can come out here and we can see that it's created a new Prisma folder inside this prismarck folder it contains the schema.prisma so this is schema the Prisma contains all our tables so uh it generates the all the configurations for the mySQL database okay so uh one last thing here is we can see that it's actually taking the URL from this environment variable database underscore URL and where does this database underscore URL comes from it comes from the planet scale a connection screen so I'm going to press here connect and I'll just create a password just leave it as default and then we can see that here this database uh URL has been given to us so I'm going to do is I'm going to copy this line I'm going to come back to my DOT EnV right and this database URL I'm going to come down and copy my my actual Planet scale string and then I'll delete the one on top so then I'm going to save this file and so now the database has been loaded in and we can also close off the planet scale database because we reset up the connection so now all we need to do is come now here to Source let's create uh let's go down to the lip folder so this zip folder will contain all our uh Library preparation files so in this case I'm going to create a new file called the db.ts so these are database.ts file okay so uh for this db.ts right for this DB dot yes we're gonna uh I'm gonna copy in this code so I'll leave this code in the page being in the description but let me go to what is this is to prepare the Prisma connection so this will only run the server okay and what we are doing is we're going to declare a global type called Cash Prisma right and then what is a prisoner client that is imported from the Prisma client type okay so what we're gonna do is we're going to play export a variable called Prisma right it's gonna be a Prisma client type and basically what it does is because we are doing development right the server is always going to restart for the hot reloading right but then when the hot reloads uh the hot reloading works is that you will try to create a new Prisma instance every time it reloads and it's very wasteful of resources that's why when we are in the what's right that's why we when we're in the development process we're going to check if there's already a global cash Prisma instance so if there is already a cash personal instance in the previous reload we don't want to create a new Prisma instance therefore we're just gonna set the Prisma variable to the cash Prisma if there's no cash Prisma then we'll initiate a new Prisma client and then when we're in production mode we actually just set the Prisma to a new Prisma plan so this is just uh all it does is just exports a new Prisma client out of this file and then we can actually this uh Prisma client in anywhere in our project to interact with the database so leave this in the description below okay so now that we have set up Prisma let's just now actually set up next auth itself so I'm gonna basically uh come down to this uh next auth uh instead of the next off let's go down to schema Prisma so this schema the Prisma uh we're able to set up our user models so we represent the user model in terms of so when we do a model and we can name it whatever we want so model uh increase my actually Maps tool or you can imagine as a table in the SQL database then we're able to give you different attributes like ID ins and then we can set it as ID and it's an auto auto increment default with all this email and name so what I'm gonna do is I'm going to basically copy paste in whatever I have here and I'm Gonna Leave This in the the description also so this is just the default next OS configuration so for next off it requires a accountable sessions table and the user table so these sessions will actually is what is used to actually manage the session so that we can purchase the user login to our website and so underneath here we just need to add something else we need to add index so this index is only for the uh the planet scale database because the planet scale database does not support certain relations so I'm gonna just index the user IDs right so I'll leave all of this code uh in the description you can just copy so how did I actually get all this code so if you come down to the uh next auth next auth uh Prisma Prisma adapter so we can see that there's a lot of there's a lot of the documentation here so like you can see this schema the Prisma this account session end user so I got all this code from here that I modified it a little bit more so I'm Gonna Leave This in the description so that you're able to copy okay and the last thing here is under this user right I'm gonna add one more attribute so so I the user has a name email and whether it's the email has been verified and of course we have the image right here right so one more thing I want to add is actually the credits so we know that for a given user a user can have by default 10 credits then as they generate the courses we're gonna document the credits right so what I'm gonna do is I'm gonna add just an attribute here no called credits okay and these credits will obviously be an integer and we'll leave it the default to be 10 so as they use it as they first create a new account they're gonna basically have 10 credits then as I uh use more and more of their credits it's gonna drop down to zero and then we'll add a check to prevent them from um creating any more courses If eventually they get to get down to zero credit score so then I'll save that and so to actually push these changes up to our planet scale database we're gonna do MPX Prisma DB push so this is a really neat feature about Prisma is that with just one command we're able to sync up the entire mySQL database with whatever configurations we have written here in the schema.prisma so now that we have that schema the Prisma setup for next OS I'm gonna come down here I'm gonna come down to the lip I'm going to create a file called auth.ts so this is going to store all our basically store our next auth configurations so I'm going to install the next auth package first so you can do npm install next Dash auth Okay and another thing I need is the Prisma adapter because how Nexus is gonna interact with our database is through Prisma therefore we need the Prisma adapter to do that so I'm going to come down here and also install mpm install and next uh dash off Prisma Dash uh depth term so let me spell that right and then under this all a TS file we can actually begin writing the code to actually configure next off so let me just close this down and let us type it out so the first thing I'm gonna export is uh basically off options so this is going to be extra configuration it has a type of next of options right it's going to import it from next auth so what is this so the first thing here is for the session we'll be using a strategy of JWT Json web tokens right so then after that for the callbacks it's gonna be an object so this is gonna be a bunch of function so the first function is the JWT function itself right so it's going to be an async function async function we're gonna basically destructure the token from it and then let's see what we return so the first thing we're going to do is we're going to find the user in the database with the particular token so do cons DB underscore user equals to await uh Prisma Prisma so we can see that Prisma is imported from the DB file that we just created just now prisma.user dot find first right where the email equals to token the email it goes to token dot email right and then we're gonna check if the DB user access we're gonna set the token.id equals to DB user.id and then we'll set the token.credits equals to DB underscore user.credits credits so what this does is actually it just tells us that uh if we are given the JW token right the token will have the ID the email on it so then we have to search for the search in the database for the user with that particular email and then once we have found the email the critical user we're gonna bind the I the database ID and the database users credit to the actual token itself so now the token contains the information like the ID and the credits left for that user and then at last I'm just going to return the token okay so under the JWT callback will have another callback for the session so the session is gonna be a function that destructures the session uh sorry session and token itself and what we're going to return is we're gonna check if there's a token right we're gonna set the session.user.id equals to token.id then we'll set the session dot user dot name equals to token.name and then we'll set session dot user dot email equal to token dot email and lastly session dot user Dot uh image equals to token.image session.user.credits equals to token of credits so this token right this token comes from this parameter but in this token actually returned cat is is found from this return here so we can see that in this GWT callback we have but we have bind the uh database users credit to the token.credits therefore this token or credits actually maps to this token of credits okay and then at the last one to just return the session so the reason why right now there's so many squiggly lines is because uh next auth doesn't know the shape of this user so what we need to actually do is we're going to come up here and we're going to tell next us the types so we're gonna declare the module of next off so we're going to be extending the types of the next off package we're gonna extend the interface session extends the default session from Nexus itself we can basically tell you that inside the session the user will have ID as a string and it will also have the credits as a number okay and then uh for this decline module and then underneath that we'll have declare another module uh and this time you'll be for the next Dash auth JWT so for the the token itself we also must extend the the types so we tell it that the interface of JWT will have ID of string and credits of number so with that we can see that now the squeaky lines goes away right but right now this name also exists right this name is also showing the error because we have overridden this user so one ninja actually do is other than this ID and credits we actually want to join it with the current user type so we will end it and default session will index into the the user object save it and then now if you come down here we can see that everything is perfect so this actually not session.user.image but is uh user dot if you come down here we can see that this image so the reason why it's showing the error is because this token dot image doesn't exist because the token right this token will actually contain the picture so uh when we pass in the session users image we actually want to get the token.picture so it's just a different naming but we're both talking about the profile image that will come from the Google authentication so then we return the session then underneath here we'll pass in the secret variable as process.env dot next of secret so let's create the next all secret by coming to the EnV and let's create the next auth that secret underscore secret you can just name it whatever you want I'm gonna name it like this I'm gonna close it okay so this secret is because it's secret we need a to actually tell it that this will definitely exist okay so then after the secret let's put the adapter right this adapter will be the uh Prisma adapter that we import from this next off that's Prisma adapter library and we'll pass in the Prisma so this Prisma actually comes from uh the DB file that we created just now and then under the adapter let's tell what we the providers that we have so a provider is like a authentication method so in this case we only have the Google authentication so what we're going to pass in is uh we'll pass in the Google provider so let's import the Google provider up uh on top first so import Google provider from next Dash auth slash providers slash Google and then underneath here we can then tell the providers we want to pass in the Google provider right and then this Google provider makes uh two things we need a client ID right and also the client secret so this client ID and client secret comes from this Google client ID and Google client Secret okay so let me just see why this is not working here this secret uh secret process.ef.x auth the secret uh everything looks good process uh okay I see the issue so you can see that this callbacks right it should not be wrapping the entire thing here right so we actually have to move uh this few configuration down one layer and then now it's correct so this callbacks is we can collapse it here so this callbacks contains the jwd function and the session function where else outside here we we put the rest of the configurations so right now let's actually create a Google client ID and Google client Secret so what we're going to do is I'm gonna come down to the browser and we'll do basically go to the console.cloud.google so this is your Google Cloud console and you have to log in with your Google account so I'm gonna create a new project here so I'm going to press new project I'm going to name it learning uh Dash Journey Dash YouTube okay then choose your billing account and then I'll press save it okay so it okay so we can see that this Learning Journey index YouTube has been created we'll press select the project so then we are now viewing the project learning Dash Journey Dash YouTube okay so the first thing here is I'm going to come down here to the sidebar I'm gonna come down to API serve and services and I'll click on credentials so before we can actually create the like this client ID and client secret we need to configure the content screen so this consent screen is what you see when you click on sign in with Google it brings you this to this page where it's asking for your Google account so the user type you can choose external so we basically allow anyone to log in we'll press create and share for the app information let us name it uh Learning Journey YouTube for the user support you know just choose your email uh we do have an app logo which is fine for the app domain if you have anything you can add here you can add here and for this authorized domain we'll leave it blank and this email address just put back your your personal email super passive and continue it's gonna configure of content screen for the scope we can just leave it uh blank just leave it empty for the test users we'll just save and continue so because this will be made public so we don't have to add any test users and then in the end for the summary we'll just click back to dashboard so we have now configured our content screen we can now actually create the client ID and client Secret children come down to this navigation menu again come down to credentials and now we can create a new credential we'll be using oauth client ID right it says request user content so that your app can access users data which is exactly exactly what we want we want to access like the name and email so with that we can now create a web application just name it whatever you want to name it learning Dash uh Journey that's YouTube for this your ID is important so we need the HTTP colon success localhost 3000 so this must actually exactly map to whatever you have here so if you're running this on baby Port 3001 you have to enter HTTP colon such as uh 3001 and for this Auto rise a redirect URI we have to enter HTTP colon localhost 3000 slash API slash auth slash callback slash Google okay so this is a handle my next off so next off whenever we redirect so after Google has signed us in It's Gonna redirect back to this endpoint and then next auth is going to handle all the rest of the configuration for us so make sure you have this exact line here and then press create to create the client ID okay so now we can see the oau of client has been created the client secret and client ID has been created for us also so I'm gonna come down to my EnV file I'm gonna copy this client ID I'm gonna come here do Google client ID equals to copy paste in and for the Google client Secret I'm gonna basically copy this in and just paste it in here okay so let us make sure the names match so I'm gonna put the end of the quote right and then we have end of the code here so make sure everything is correct so if you come out here we can see that this is the Google client ID and Google client secret which maps to this variable Google client ID and Google client Secret we'll save the file and we can actually come down here and close this tab we no longer need this tab okay so the last thing here is we just have to tell it that this will definitely exist so just trust us right and we'll save the file and the last thing here is we have to create the next auth endpoints so under the app I'm going to create a new folder called API and under this API I'm going to create a new file called uh auth okay so uh under this off folder I'm gonna create another folder called uh foreign so what this uh what this means is that this is a special syntax within the app directory so if you have a like a square bracket and then inside has a dot dot dot next off so all this anything that is uh any route that contains slash API slash off says whatever it will be handled by this route right so this is what the World Cup means so under this root.ts for next auth let us actually import a few things so we have to import the off options that we actually specified just now uh all options from these lip slash auth so is this file that we're working on just now right and then we also need the actual next off import next auth from next off slash next and we'll just uh create the Handler as next auth and pass in the pause options are options and lastly we can actually export the Handler as get and Handler as post so these are all from the next auth documentation so basically what this does is that with this file any root that comes into slash API slash will be handled by next OS including all the Google callbacks and Google redirects so with that we will save the file uh save it and close it and with that we actually have completed the next auth setup okay we're doing great so next time I want to set up uh some basic styling so I want to come down to let me just close up all the files uh to clean it up I'm gonna come down to the safe app and come down to layout.tsx and let's make some changes here so the first thing here is that uh so this metadata is a new feature of Nexus 13.4 so the title here will actually reflect the title shown on the tab so I'm going to name it to a Learning Journey uh learning Journey I'll remove the description okay and then for this I want to change the font so I don't use enter I'm going to choose the election font from next slash font slash Google will also change this to lexin will change all the variable name to lexin so like send.clust name okay so if we save that we can see that the font has changed to Lexington and the title has also changed okay so the next thing here is under this uh body right I want to basically add some more styling so he has actually create a youtube.ts file and is exported this function called CN so what dcn function does is actually combines telling CSS classes and provides a nice utility function to actually merge conditional uh styling within 101 CSS so we can actually use this function and how I'm going to use it is I'm going to come down to the layout so for this body I'm going to just delete everything in this class name first I'm going to import this CN function from utils and then the first thing I'll pass in is the lexan.class name right and then as a second argument I can pass in anti-aliased and mean Dash height test screen and the padding tool of 16. so this Paddington of 16 will be for the nav bar so with that I'm going to save the file and now we have some padding on top okay that's good so next up let's actually set up the nav bar okay so I'm going to come down here I'm going to create under this component so not under the UI but under components itself I'll create a new uh component called navbar.tsx okay so this network of TXS let's actually start coding it out so tsra fce so these are snippet they will see me use a lot in this video so just create a new component called navbar I'll save it and then uh just basically under the layout under this layout.tsx file I'm going to actually import the nav bar so just above the children but inside the body I'll import the nav bar from the com the components we'll save it you can now see that this never appears up here so let's actually install the nav bar okay so for the next bar uh the first thing we want to do is let's just build up to the UI so I'm gonna return uh just a nav uh nav diff and just give you a custom off uh let me just see the costume fixed in set text as X of zero uh top zero BG Dash white when it is dark mode we will need to have a BG gray of 950 uh Z index of 10 right so it shows up above everything else height of fit height fit border bottom border zinc of 300 and padding y of two okay so we can see that it has this nice nav bar effect So within the nav bar within the nav bar let's add a few things so inside therefore I'm gonna have another div okay so this div uh is just gonna contain the casting of flex items Center just defy Center a full height we won't need to have a gap of two uh PX of 8 MX Auto right but when it's small when it's on the small screen we're going to justify between right and then we'll give you a Max width of 7x out okay so inside the second div here inside the second div we can basically put the logo so we'll have a P tag that says Learning Journey and this P will have a class name of uh rounded that's large border of two border bottom of four border right of four okay border black padding X of 2 padding y of one text Dash XL phones both and will give you a nice transition property transition or when it will hover over it we actually want the negative trends late y by 2 pixels okay and then when it's medium screen we want it to be uh display block okay and lastly when it's dark mode we have a model of white so let's save that now you can see that now it has this nice logo here and then when we hover away actually moves up a little so that's pretty nice okay so we can see that basically when it's on the bigger screen is all the way to the left when it's on the small screen you'll be in the center okay so this P right actually there should be another link is a link wrapping it also import this link from a next slash link so we'll wrap this a paragraph tag and so this link will just lead to the sorry this link will lead to the slash Gallery page that we'll create later okay so we'll give you a class name also of items Center and hidden when it's small screen game of Two And when it's small one is above small screen will give you a flex so we can see that uh right now when it's on the small screen we hide it then on this big screen we will show it just for the responsiveness snake snake okay okay so under this link component right uh but still uh within the div I'm going to create another div so this div will have a class name of just flex and items Center then within that div right we'll have uh we have the a new link so this link will actually lead to the create course page so create course so let us give you a href of just slash create and the class name of margin right of three I'm gonna copy this down and for the next link it will just be to the settings page and other than instead of create cost it will just be settings so let's save that and we can see that now we have this uh these two links here that will bring us to the settings and create course page okay so underneath here uh we actually want to create the user account nav right so let's also actually create a sign in button so I'm going to come down here to the file I'm gonna under the components I'm going to create a new component called sign in button.tsx so this for the signing in functionality 2x off so tsrafce okay sign in button so for this sign in button uh it's gonna be a client component uh use client okay for this use client component we just want to return a button from the UI so just from and we'll just have a text call sign in and basically we have this variance of of goes and then when we click on this button what do we want to do we want to sign the user in but how do we actually signing them in so next us actually give us a function called sign in we can see that it is imports from next or slash react right with this sign in we can pass in uh whatever method we want to sign them in with in this case we sign in with Google right and then we'll save it okay so now we have this sign in button I'm gonna just close this file come down to navbar right and then under the settings I'm gonna just import the sign in button so sign in button and then I'll just save it okay all right and now we can see that now that we have this sign in button right here so if I click on this you will then bring me to the next uh Google page you can see that this Learning Journey that's YouTube is one configured for the the content screen so then I can click on my account and then it will lead me back here but right now you can see that how do we know whether we are signed in or not right so what we can do is let us come down to go back into the auth library uh so this is where we store our auth options right so for this uh us options I'm going to export another function down here export cons gets off session so this is gonna be a utility function that will return us with the session of the user so we'll return get server session so this function is imported from next auth and we'll pass in our sorry pass in our aux options so this these OS options will be from this here so with this function you will run on the server and it will return us with the user if it's logged in or with an empty section if the user is not logged in so we'll save that and we'll come down to navbar so right now this navbar is a server component meaning that this network this network code will run on the server so for this next year's 13.4 functionality we actually Mark these components async and right here we can actually uh get the session so we'll do con session it goes to a weight get off session so this get off session is the function we have written just now and then we actually just console the log out the session session so I'm going to open my terminal okay I'm gonna close it here I'm gonna see what the session is being logged out so I'm gonna expand this expand this so if I save this right we can see that this session is being logged out here so this session contains my user right my name my email my image and my credits which is 10 okay so with this information we can then render if the user is signed in we don't want to display the sign in button right so the closest terminal right and then under here I can actually get access to the the user so I only want to show the create course and the setting uh settings page if the user is signed in right so I'm gonna come down here under uh under the gallery component under this uh settings I want to just let me see here okay so under this uh Flex item Center we have actually another link right the links to the gallery page and of course you just need to slash Gallery with the class name of margin right of three save it so these two links this create course and settings a link I only want to show it if the user exists so what I'm going to do is session dot uh dot user so if the session uh if the user exists within the session that means they are logged in then only then do I want to show them these two links right okay so let me just wrap them in a react fragment so that we don't get this error okay so now we can see that if the session the user exists then we can see this create course and settings exist but for this sign in button right we want to display only if they are not logged in so what I can do is I can just do uh I'm gonna create a new div with a class name of flags and items Dash Center and then under here I can check if the session.user exists with the display otherwise we can display the signing button right then I'm gonna just come down here and just enter this uh signed in save it so if the user exists that means that log in will show the sign in text even though we show them the same button and you can right now see that because we are signed in we have shown this a sign in paragraph tag Okay so uh let's actually also create a sign up button right so I'm going to create this new component called the user account nav so if you come down to the original settings we can see that we have this profile picture and when we click on this profile picture we can see that there's this nice drop down menu so be creating this component right now so underneath here I'm gonna come down to our directory let's go down to components and create a user account nav dot TSX tsrafce okay and then let us import this user account nav so if the user is signed in instead of showing this paragraph let us actually show the user accounts nav so this is what is the profile image and the manual drop down later that we'll see okay so save it and now we can see it's just a user account nav so now let's actually build up this uh component itself okay so this component is going to be a client component so up here I'm going to press use client okay so this is next year's 13.4 feature so this user account nav is gonna just have a drop down so this drop down a component actually comes from chat CN so what I'm gonna do is I'm gonna install that component so I'm going to come down here I'm gonna do menu so we'll press yes for you to install so it's going to try to copy in the code from into our component file so you can see this uh drop down menu has been downloaded into our file okay with the drop down menu let's actually uh do the do the user so I'm gonna just return the drop down menu so you can see that it's imported from the UI folder and this drop down menu will have a drop down menu trigger right also imported from the UI folder inside here we just have the image right so right now we don't have the user image I'm gonna leave it as just image right let's just leave it as a button here uh your access button say uh open menu okay and then under this drop down menu trigger we have the actual drop down uh drop down menu content imported from UI okay so for this Dropout content we'll have a alignment of just n okay within the content let us have a div right this div will have a class name of flex items Dash Center justify start gap of two and padding of 2. within that div so we can now see this uh this thing working and we have this uh this content here so within this content uh let us actually display the user's name okay so how do we actually get access to the user's name here we can actually get it from the props so let me just pass it into the props so this prop uh this user is gonna have a user and how do we actually get this user object so we can get this user from the next auth type so remember there's two right this user is from the Prisma client or the next auth so in this case we want the user because this user is from the next auth session therefore we import the type from next auth okay so we have this user and then now we can actually come in here and then we want to style it so we have uh for this year we have this user.name sorry so actually this structure this from the props here user so we just check first if the user has a name right then we want to show just a paragraph tag of just the user's name okay we'll give you a nice styling of just fonts medium so if you save it right now it's going to throw an error right we can see that it's not left by string and error because this user account nav right it requires this user prop however we're not passing it in right so what I'm going to do is pass it in we're gonna pass in the user object let's just pass in the session dot user okay we'll save it and now if you save both file we can see that my name shows up here so this name shows up as the session from the session okay that's Corner coding so underneath uh inside this div we'll check for the user email so if the user has an email right I'm gonna display a paragraph tag of the user's email and then we'll give you a class name of just width of 200 pixels okay a trend kit text Dash SM tax secondary Dash foreground let's save that and we can now see that we have this uh showing up here okay that is good that is good items are not just if I start get the shoe so I'm just making sure everything is correct okay so up here so below this opening div above the user's name I want to create another div right so this div actually wraps both of these username and email okay so within this div let's give you a class name of flex Flex Dash core column right and then space Dash Y dash one and leading of leading of uh leading of none so let's save it we can now see that we have this a nice menu that shows our name and email okay okay that's good we are doing excellent so I'm gonna just collapse this div so underneath the second div just before it's closing I'm gonna add a drop down menu separator from the UI file and then I'm gonna come down here have a drop down menu content uh not content item from the UI again so this item will contain a uh the sign out button right so we'll have a sign up so we'll save it and we can see that we have this sign up button so when we click on this button we actually want to sign them out right so how do we do that so on here we can have a on Select Property so when we select right we can actually just log them out so how do we actually sign that out so next I'll provide another nice function called sign up right we can just import it and just call the function so we've called this function it will just sign us out right and for this uh sign up I'm going to add a logo just beside it called log out so this lockout comes from Lucid that's react I'm gonna just import it we'll give you a class name of with Dash 4 height Dash 4 and margin left of two right for the drop down menu item I also want to give you a class name of text Dash rate Dash 600 and the cursor pointer so just give you some styling so now it looks a little nicer so when we click on this side now it's going to sign us up and now we can see that because we are signed out right he's asking us to sign in right so so that's working perfectly so then we press sign in again it will direct us to the content screen we log back in and now we can see that the menu works again okay that's good that's awesome so last thing here is instead of this open menu button on the display lead the profile image so we'll make that into another component okay this component is gonna call because uh user Avatar so other components I'm going to create a user Dash a user avatar.tsx file tsrafce okay so this user Avatar will have a few things so obviously we want to import we want to download the Avatar component from chat CN so we'll do MPX chat CN Dash UI at Avatar so this is just an avatar component from reset CN itself we press yes to install it okay so what is installing let's actually code it up so I'm gonna return an avatar from the UI Avatar folder right within the Avatar let us check for the user's image so how do we get access to this user image right we can actually receive it as a prop so let's do user as a user type from the next auth type and then let us actually destructure the user so we'll just check if the user.image exists right we want to return a div okay if the user image doesn't exist we'll return the Avatar fallback right this Avatar fallback will have a spend and then we have the class name of screen reader only I'll pass in the the user.name so these only for screen readers and then inside this div let's actually display the image so we have a this day will contain the class name of relative with a full height of full in the aspect of square within the DF let's show the actual image from the next Dash image so let's import it from uh sorry next next Dash image so this next text image will have the field attribute The Source will come from the user.image and then lastly the out will be the user profile and referral policy we'll just leave it as no referral okay so right now if you save this it's going to throw an error okay let's see why okay so let's just import this user Avatar so inside this user accounts nav instead of showing this uh ugly button we're gonna show the user Avatar right we need to pass in the user prop right so passing the user and we'll save it and look at the error that is true okay so in value Source prop whatever this is so all we need to do to fix this error is come down to our next Dot config.js and under the text config we'll pass in the image images right we need to basically white list the domain so this domain comes from this uh lh3.google user content I'm going to copy this in we'll just white list this domain by saving it and we just need to restart the server whenever we upload we change the next config so do npm run Dev again okay so now it has restarted so let's save the file and refresh the page and let's hope that it works and perfect is working so now we can see that our avatar shows up here and then our information is here and we're able to sign out okay awesome so next I'm gonna show you how to add this theme toggle for the light and dark mode so I'm gonna refer to the chat CN documentation so if you come down to the documentation on dark mode right for next year's we'll just follow whatever they are saying so the first thing here is I'm going to come down to my file right I'm going to create a new component called uh providers.tsx okay so these providers will create the will be the team provider that wraps our entire app and basically allows all our components to know whether it's light or dark team so I'm gonna just copy the code here uh copy it and paste so it's a client component right and we need a few things we need the next teams package so I'm gonna come down here and just uh move this to the site the install and you can install uh next Dash teams okay so while that's installing we'll wait for it to load in okay and then uh we need to actually wrap our layout with this team provider so I'm gonna come now I'm Gonna Save the file I'm going to come out to my layout.tsx and then uh basically for our other wrap our entire uh nav bar and our children with the the provider providers so come down to Providers we can see that it's actually export this is function called team provider also rename it to provider save it so come down here and just see provider so we import it and we'll wrap our entire app with these providers uh let me just copy this in sorry okay I have it okay we'll save it so now this provider is wrapping on therefore and children right okay and then underneath this team provider underneath this uh provider this team provider we can actually pass in a few things so the first thing we can pass in is the attribute of class in the default team of system so we respect the user's decision and also want to pass in the enable system attribute so then we'll save it and then right now we can see that there's not much difference so we create the actual mode toggle this button here to actually switch between like end up team so I'll create a new component uh this component will be called team toggle.tsx we'll come into the code we'll copy it from sets in we'll paste it in okay I'm gonna make a few changes to it so right now you can see that uh it's using the use team right I'm gonna just rename it to team toggle so for this team toggle Wanga also do here is I want to wrap this in entire thing into div and then we'll wrap it up here we'll save it so for this component we want to make it reusable and uh very reusable right never want to pass in custom class names to style this component uh through the parent so what I'm gonna do is it's gonna receive a prop which can receive the props so what's this props be called this props will come from the HTML attributes uh and then it's gonna have HTML the HTML div element so what this does is basically it's telling this component that you receive any props the normal div element will receive right which will include class name right and we'll then restructure the rest of the props okay so for this diff I'm gonna just pass in the class name as a class name that's received from the prop and we'll pass in the rest of the props okay we'll save it and then let's close the file closest providers and so just beside this uh just beside the sign in button right I want to display the uh the button for the toggle right so just send it back to my account so that we're able to see what we're working with so close the user adverter so I'm gonna come down to our a nav bar code again so where do we place the team toggle right we'll place it just above uh this div here for the session the user session so do team toggle right imported from the component and we'll just close it and save it and now we can see that right now we have this uh this button so just remove this th and let us pass in a class name of margin right of three so you can see now that if you do control space it accepts all these props and these props are anything that a div element will accept and because we're passing as props right if you go into the team toggle it will then be passed into this div okay so now we can see now we can switch between light and dark team and the system team okay so perfect that's good we have set up the next team okay so we're doing well so now let's move on to the course creation page so when we go to the create course page right we want to we'll make this form so let's do this uh create route so we can see that it's at the slash create so how do I create a new route so we'll come down to our directory so under our app directory let us create a new folder called create and inside this create we'll create a file called page.tsx okay so let me just save this so under this piece of TSX let's just do tsrafc to create this component and let's just call this a create page so let's save it okay so now whenever we navigate to uh Slash create right uh it will show this page this create page here okay so let's do uh some of the styling needed so first thing is gonna uh just be a server component so if the user is not logged in here we just want to redirect them back to the gallery page so how do we get the user session we do con session equals to await get off session so this this function is imported from our auth our auth library and then we check if not session dot user so if the user is not logged in we want to return a redirect so this redirect comes from the uh the next notification so redirect from next slash navigation okay so we just want to redirect them to slash gallery okay so if they are logged in we want to return the form so we'll just return uh a big diff so this diff will contain uh Flex Dash call sorry the customer Flex Flex call items start Max width of XL padding X of 8 MX Auto margin y of 16. and when you're small we just have a piloting X of zero within this div let's have a H1 that says Learning Journey this H1 will have a class name of self Center text of 3XL fonts bold okay text center right when it's small we do stacks a 6xl so let's save it and right now we have this thing here okay that's good so underneath the H1 before the closing let us create the this card so it's a card right for this information so this card component will actually uh come from uh actually you can just uh put div here as a card so this div card will have a class name of let's say Flex padding of four margin top of five border of none and uh background secondary second re within the div the card let's just have an info logo this info will come from a lucid react so let's uh import it from Lucid search react okay uh info icon and let's give you a costume of width of 12. head of 12 modular of three and text blue of 400. let's save it okay so we have that right now so just below the info uh icon let's have a div and in here just type in the information like enter in a course title or what you want to learn about then enter a list of units which are the the specific fix you want to learn and our our AI will generate a course for you so let's see if this formatted and now we got this a nice card that representables what we have in this example okay that's good so one more thing I want to add here is uh just underneath here so underneath the closing div here so just just below the card here here is actually where we want to put the form right so this form we're going to extract out into another component into a client component so what we're going to do here I'm gonna create a new component I'm going to create create a course form dot TSX tsrafc okay then go back to my page and just uh import the create course form here okay that's good so now we have this course from here and we can continue working on the course form okay the first step of the course form is let us just make this into a client component by adding the use client directory on top okay so first is let's just do some styling so I'm gonna just return a div right the div is gonna have a custom off with a full okay so here is where we actually do the form so for the form I'm going to refer to the chat CM form and see how they do it okay so for this form we'll be creating the end-to-end type save form and actually I've done a good example on how to create a end-to-end type save form using chat scene and react code form you can view the video up here so I'll just go through it here so the first thing is to actually install the form component from so let's do that we'll do MPX chat Cs and UI latest add form okay so let's just install the dependency and next we have to create a form schema okay so this schema will be using the Zod schema language to Define uh what what what's the shape and what kind of things do we want to submit in the form okay so to create the schema let us come down to the the out directory and let's go under source and go on the source so we are in the source folder and let's create a new file just called uh I'll say validators okay so within the validators we can create a new file called course the TS so this is where we actually Define the shape of our form okay so we'll be using zot so the first thing let's import Z from zot okay and then let's export uh create a create course sorry create chapters schema it's gonna be a z dot uh Z dot object okay So within the object we have a title which is going to be a z dot string okay we have a mean length of three and the max length of 100 also we have a bunch of units so the units is going to be an array and it's going to be just an array of strings okay so let me just show you what this maps to so if you come down to our original example we can see that we have one main title and we can have multiple units right we can have as many units as we want therefore these units will be stored as an array of strings and where else we have one main title for this main title the main title unit okay that's good so now that we have defined the schema let us continue with the hook the form so the next thing is let's download this react hook form component so we'll do npm install a react hook form okay and then we actually need one more thing we need this ad hook form slash resolvers so npm install a hook form slash resolvers so basically because this hook form a react hook form can actually take in multiple validators like uh like there's another package called a yam or Yap I think and so this uh resolver this hook form size resolvers will basically link up our schema to the actual react hook form validation so now let's actually uh let us set the the form so I'm gonna continue down here so now we can actually start the form so since we have downloaded the form component from chat CN let's get the react uh this form from the UI form and within it let us just a clear form field uh create a normal HTML form so right now this form is commercial cleaning because we do not have certain properties so these properties can actually be deconstructed from the react form so the first thing here is I'm gonna uh get the input so type of type of input so this input is basically the shape of our form which we have it from our validators here so how do we actually get the typescript type so right now this is a zero object it's a result object how do we actually create a type from this so what we can do is actually let us just do Z import that from sort so Z has an infer uh function right which can take in the type of I will create a chapter schema that's imported from our validators now if you see it after we hover over this input we can see that it's not converted into a typescript type right where we can see that the title is a string and the unit is a array of strings so with this input we can actually initialize react hook form so the first thing you want to do is we declare the form variable from the use form so this form will come from react hook form we're passing the generic of the input so we are basically telling react hook form that we expect this input uh the form to have the shape of this input here okay so within this uh there are a few configuration the first configuration is the resolver so the resolver is gonna be the zot resolver so let's import the zot resolver so import uh salt resolver from a hook form slash resolver slash result okay so then we can tell the react hook form how resolver is actually using the zot resolver and we pass in the create chapter schema okay and then we can pass in another option for the default values right so actually just let me just show you typescript so if I do contract space uh control and sorry control and space we're able to see there's actually a lot of different uh attributes that we can put in this object so we'll put in the default values okay so if you do control space again we can see that we need a title in the units so as a default uh default value for the title I'm just going to put it as an empty string and for the units I want to have an array of three empty strings so these three MD strings will basically correspond to the m the three empty inputs right here and the title of this ambition will represent this input right here okay so later I'll show you how to dynamically add this animation and to create more units okay so now let's continue bringing out the UI so now that we have this uh react code from a form variable within this form we can actually restructure the the form and spread it out into this form so now it will stop complaining So within this form the HTML form element we can also pass in the on submit property so whenever we submit we take the form the handle submit and we're passing our own custom on submit function okay so let's create this function on submit function right it's going to take in data it's going to have the type of input okay so let us just this unsubmit it's going to be just a normal function I'm going to console.log out the data okay so what this does is whenever we submit a form on submit we can see this form variable is coming from this react hook form so we have this from the handle submit is the react hook form function and we're passing in our own custom on the submit function so basically what it's saying is that whenever this form submits right it's gonna run the validator so it's going to make sure that we have uh all the inputs are in the correct format and then we'll call this on submit function which will take in the data and basically since we have this input type this title and the this title and the units we can actually have the type type annotation so input uh sorry this uh like data dot title or unit so we get this type safety out of the box and we can just console the unlock the data whenever we submit okay so within this form uh let me give you a class name also so this class name will be with a full and margin top of four okay within the form let us have a form field component from the UI Library so this form field will just be a sorry form field let's make it into a solve not a self-closing type uh a is a self-con closing tag so we'll pass in the control the control will come from the formula control so this form as always is from the react hook form up here so all of this actually is I'm just referring to the documentation here so we can see that the form on submit form field and control is everything is on here okay so we'll continue on with creating this form so uh this form control will have a name of title so you can see this type annotation because in the zot schema we have defined two attributes only so it can only be either title or units so title we put in this title so right now we are creating this first input for this title box here okay so other than the name of title uh let's let's render out the actual form so passing the render attribute so for this render attributes we can actually destructure the field from it and we can return a form item okay and so this form item uh let us import it and this form item will have a class name of flex Flex Dash call items that starts uh with a full when it's small we show them as items Center and also Flex row okay within the form item let us put a form label okay so this form label will have a class name of flex dash one in Brackets right so fax call facts row so the factual will only be applicable when it's above the small screen so let's continue so other than the fixed one this form level phone level also have a text Excel and for the form label let's just put the title title below the form label let us actually put a form control okay imported this phone control will have a class name of flex-6 so basically what he's saying is that this phone control will take up six units of space so this will be six units of space versus this title only be one unit of space that's how the flexbox works inside this Form controller let us put an input field so this input we also need to install it from chat CN MPX chat CN you like UI at latest add input okay so then we'll press yes so after installing it we can import this input field from the input file let's give it the placeholder of let's say enter the main topic of the course okay and we'll just pass in the rest of the field that's distracted from this Okay the reason why this render uh here is throwing error is let's see why okay let's just save the file first okay and let's look at the error so let's scroll down let's go down okay so uh because this render is showing error because right now see we're having this uh curly braces so that means everything here is actually not being returned so all we do is let us just uh do the explicit return okay and then now the arrow go away so now see you can now see that this button this field is being rendered okay that's awesome so good work so now we have this title so we can actually just also do console.log uh so remember this form a variable it also create gives us a nice utility called watch so if you save it and let us open the console right we can see that this is what is being considered along so right now the title is empty and our units is 3md shrinks so as we type out the title we can see that the state is being changed and updated as we type in so now we can take this input and later submit it to our backend so let me just uh just do form.watch right but remove the control.lock so I'm gonna save it okay so that's good so after this title now let's actually do the dynamic uh adding and removing of the units with these two buttons so we can see that this is really a nice animation which is actually using framer motion but let's just do the UI first then we can talk about the framework motion later okay so uh underneath this uh underneath this form field right under this form field so it's still within the big form right with under the the big title form here let's create the the individual units so what we're gonna do is I'm gonna just do a form dot watch these units right so I'm gonna look at this unit so right now this unit is gonna be a real of three empty strings so for each of these MD string I'm gonna render a input field right so for each of these strings I'm gonna do dot map right so I can do the dot map so I'm gonna take in the the first argument and the index so I'm interested in the index and then let's just do a return okay so what do you want to return we want to First just return a form field variable right self-closing tag so this form field will have the control will be the form.control and then for the name let us just pass it as units dot index so this is a special syntax within the react hook form right because this index is this index is uh we are changing the individual index of the arrays right so like let's say these units are the index 0 index one and index two right so this is index one index index zero index one index two so whenever we make a change here we actually want to update the correct index within the the form State therefore we're passing this special syntax of units.index right so then we can control the individual index within the state so after the name let us also actually render out the form so passing the render prop right this render prop is going to take in the field and we can destructure it is going to return just a form item right it's the same thing just now right let's pass in a class name of class name of Flex Flex just call items start right it's gonna be very similar to the things yourself items start and also um with full many small run items Center and when you're small we also want it to be fixed roll okay within this uh form item so let me just save it oh here right now it's showing error because missing key prop so actually this form field we can also accept the key we can just put it as the index okay that's good so inside this form item right we have the form label right this phone label is just gonna do units and the index plus one because the unit index is your base but I want to show it as one two three therefore we add one to it this form label is going to have a class name of flex that's one and text of excel so similar to the one above just now then we have actually the form control over here the phone control will have a customer reflection there's six so it's gonna take up six units of space compared to the one one unit of space up here inside the phone control let's put the input right self closing let's give you a class name of just actually we don't need a class name for this so we'll just put it as the placeholder let's just say enter subtopic of the course okay and pass in the rest of the field okay so now let's save this and we can now see right now we have this you need one unit two and unit three right and because this is mapping from the unit state so this is the initial state so if you want to start with four units to begin with we can add one more you can now see that I will refresh the page we can see now it starts with four right because it's mapping from this uh four empty arrays so let me just put it back to three okay let me just refresh the page okay now let us actually create the the buttons here to actually add more units to it okay so underneath here underneath the uh form actually let me see if it's still within the form okay it is still within the form right so it's underneath the map so if you do thumb dot watch here just below the map let's create the buttons so we'll give a div here and the class name of flex items Center justify Center and the margin top of four within the div let us uh let me just install one component is the separator MPX chat CN let's add a separator component from I'm going to press yes okay so we have this separator component from the UI folder let's give you a classical Flex of one okay so let me show you what I try and do here so we can see that right here we have this separator it's a bit light I'm not sure if you can see it but there's like this tiny Gray Line here and then with the two buttons and another tightly green line here so that's the separator okay so just below the separator or beside the separator will create a div another diff and this diff is going to house the buttons like the remove and adding of the units a Max of four within it let's put the button import it from the UI and let us give it this uh this variant of sorry variance of this success sup is a success nope a secondary I mean and then we have a class name of fonts semi ball within the button let's just put as ADD unit so let's save it first and now we can see that we have this button here so let's make it more similar to this button so beside uh the button here we can also add icon from plus so this comes from Lucid react we'll give you a customer of width search for high Dash 4 margin left of 2 and also uh text green of 500 save it and now we can see we have this nice icon here okay that's good and then below this button let me just copy this button down one more so this is for the removing of the unit so instead of adding unit we'll just remove unit of plus let's import the trash icon from Lucid react instead of being green color let's make it a red color so let's save it and now we have this thing here okay so for this button I'm gonna add another custom for margin left of two so just to space it a little nicer and I think that's it so okay then underneath the div so outside of the button outside of the div we have now separator with the flags of one save it and now we have this nice effect where the buttons are centered okay so let's add the logic such that when we click on ADD unit we actually uh add another unit below so one more thing here is when we press on this button right now it's trying to summon the form which you do not want to therefore we just need to add the button we just add a type of uh of just a normal button so you won't try to submit whenever we try to press the button so passy as type of button okay that's good so now how do we actually add the topic so whenever we click on this button on click right what do you want to do we want to manually set the form state so when to manually set the form step state to add another empty string to the units array right so we can do that by doing form dot set value right we can see that we are setting the units and what we're doing to set it to we're going to set it to the right now the form dot watch of units right so this will return us with the current array of units we spread it out and then we just add one more empty string to it right so this will basically a pen one more empty string units to the units array state so if we save that now when we click on the add unit we can see the unit gets added right because we're appending an empty string to it and then then we're mapping through all the units then we get these nice things then we can do the same for the remove unit so underneath the button for the remove units whenever we click on the remove unit we just want to do form dot set value for the units uh units let's just do form dot what sorry uh form dot watch units right we want to just dot slice so slice will just basically take a sub set of it 0 to negative one so we're basically removing the last element from the units array and so that will create this a nice uh see so now it works so whenever we remove the unit you will just do it so it comes out of the console let us try logging out the the state so that we can see it so if you do control the lock from The Watch right we can now see right now as we move it we can see the title is being changed and if you change let's say the four units so let's go into the units right now we can see the units is empty strings right but let's say I change this subtopic we can now see that within the state the second element will have the DWD which is what is entered here so then if I add one more unit we can still see that instead of four units we have now five units of empty strings so I hope you understand that that concept of what we're trying to do here okay so let me just add the framement motion now so right now we can see that it's very uh jarring right because whenever you add the topic there's no animation between it once in the original example you can see that there's this this nice floating animation to us to show the removal and the addition of the units so to do that let us go and install the framework motion Library so mchem install framer Dash motion Okay so I'm gonna import uh something from the Freeman motion Library up here so let's go all the way up we need to import the let me see from framer motion okay so let's see what I need to import we need to import two things first is the motion and the second is the animate presence so these animate presence presence is what actually does the animations so whenever a unit gets added right the the element gets added to the Dom it will then animate uh using the animate presence a utility from the framer motion so I'm gonna come down here all the way down here right this from the watch so instead of returning just a normal form field right so I'm gonna wrap this entire thing in a animates a presence a thing I'm gonna wrap it up here so what I'm trying to say is that whatever elements get that gets added or removed in this uh children will be animated properly okay so within this form dot watch instead of just returning this form field I'm gonna wrap this into a motion dot div so this motion.diff is a special div that will enable the that will enable the animation so I'm gonna wrap it here okay and right now this is telling us that we don't have the key so let's pass the key to the motion dot this key equals to index okay so this motion.diff right now if we save it we can see that uh right now still doesn't really work right because we still haven't added our animations so these motions are diff I'm gonna add a few things the first thing is initially it's gonna have a opacity of zero and the height of zero then as we animate it animate it we want it to have the opacity of 1 and height of Auto so that it expands all the way up and then when we exit that means when it's removed from the Dom we just want it to set back to opposite opacity of zero and high of 0. and then lastly for the transition property let's pass in opacity of just the duration of 0.2 and then for the height we also transition over the duration of 0.2 so now if we save it we can now see that we have this beautiful smooth animation for when we are removing and adding the units okay that's good okay so we're doing well so after this unit let us actually create the submit button for uh for the submission so just underneath the uh just we are still within the form but inside the form let us actually add the button so we have this separator and underneath the div so just before the form closing let's add another button here right this button will just have a let's let's go right and then let us give you some styling so we have the type of it's a submit type of button and then class name of with full and margin top of six will give you a large size uh LG and then yeah let's just save it first and now we have this button okay okay so that looks good so now let's talk more about the back-end data infrastructure so I'm gonna just show you the relationship between uh so if you come back to the original Gallery here uh here so we can see that there's actually three main ideas that I need you to understand so what's the difference between a course a unit and a chapter so I'm going to show you the data modeling for that first right so if you come down to let me just draw it out for you right so the difference between we have a course we have a unit and also have uh chapters chapters okay so the main idea is this one course is like one course is basically this whole thing right whatever you see in this one card so this one card is a course that's about let's say a next Js within one course there may be multiple units right so we can see here we have unit one unit two unit three so this each unit will have let's say this first unit is about next year's versions the next user is about best practices in the last thing is about deployment okay so each course can have multiple units right so it's like this this is a hierarchy so one course is the biggest uh biggest item up here so each course can have multiple units and then within each unit we have multiple chapters so chapters is the smallest part of it so this chapter will contain the YouTube video the summary right and then also the quiz the questions so this uh unit let me just make this into a smaller font so each unit can have multiple chapters underneath it like this so chapter one chapter three maybe has two chapters for this unit and maybe another three chapters here so I hope that this make it makes uh the terminology clearer as we begin to talk more about the back end it end points so let us do the data modeling for this so I'm gonna come back here I'm gonna come back into my schema.prisma and now let's actually do the data modeling okay so remember for Prisma a model maps to a table within a mySQL database so a course can contain many units therefore let's create a model of the course so the course will have ID a string is ID field let's just put a default as a CU ID it will has a basically a name so this course name could be let's say a next JS so that's the name and then we have the chords uh sorry we have the image so this image is just the image string or what when you see it in the gallery gallery page like it's just this image here this image here so we'll leave it as a string okay it's gonna be a URL to the paxos API paxos URL okay and then let us also have a it has multiple units so it's gonna be an array of units so that's how we show the many to many one-to-many relationships between course and units okay and then let's leave it as that and the next step is one unit contains many chapters so let's create the unit model so the unit will have an ID is a string ID and the phobia Co ID will have a course ID so this is what is being used to link back to this course up here so each unit belongs to one course so then we have a name which is a string so this unit name could be like next year's versions next year's best practices next year's deployment then another different name it will obviously it belongs to a course right so it has a relational field that it is just referencing the course ID and then underneath that you have multiple chapters so it's gonna be an array of chapter let's give an index right at the course ID and the name is course ID so this is just the many to many uh these one-to-many relationship between unit and course and lastly create let's create the chapter model so for the chapter model the chapter is going to have ID as usual at ID of the default which is the cuid okay you also have a unit ID because each chapter belongs to one unit right and then we have a name which is a string uh the Youtube search query okay so later when we are using the AI open AI API we're gonna basically ask the AI endpoint to return us with a relevant search query such that we're able to send this search query to the YouTube API and obtain a relevant video that's why we have this YouTube search query uh thing right here so this YouTube search query is going to be a string and then we also have the video ID which is going to be a string optional right because we might not have the video ID yet so this video ID is mapped to the YouTube video so each video in the YouTube API has an ID so this is what we are using to map the video that's relevant to the chapter we also have a summary we have the summary so it's the summary of the transcript basically it's gonna be optional string and we'll have to map it to a DB dot VAR VAR varchar a wire chart of 3000 yeah so these three thousand is to make sure that we can put in 3000 characters if not it will overfill and then you'll cut off the try and create the sub summary and lastly it will belong to a unit right and then obviously it will have uh the unit here we also need to add the index so let's just do the index right that links it back to the unit okay so now everything looks good and the last part here is the question so each chapter will have multiple questions right so let's also do that part so we'll have the questions it's just gonna be a array of question model sorry so let's create a question model question okay so each question uh he has ID right same ID is going to be ID default coid uh it will have a chapter ID so each question belongs to one chapter right and then we have the actual question okay you're gonna have a DB dot uh varchar of three thousand okay uh we have the actual answer to it and we'll have options which is a string which is also a DB Orchard 3000 so these options is gonna be a Json array that will stringify it right so this will be McQ and then lastly uh we have the chapter that's linked back to the chapter relations up here and then we can also do the index to make sure everything is linked up properly so each question is belonged to one chapter and so we have done this data modeling and then now we can see this relationship so let's save the file and let's push it to our Prisma uh our planet scale database so just MPX Prisma DB push so this will push the schema up to the Prisma database so that now our Prisma database will contain all the schemas right here okay make sure it works by doing npx Prisma Studio so this will open uh this will open like a studio database client so now we can see that we have a account model chapter model course model question model so we can see that everything here has been added of all the columns names have been added correctly so that's good so then let us just come down here and let's stop the prismarck studio and let us continue okay so now we can actually do the create course endpoint so what's gonna happen is that uh whenever we create a course right when we press let's go we're gonna pass the title and the a bunch of units into a backend API and then we're gonna generate all the chapters okay so I'm gonna just come down here uh let me just create a new endpoint so to it create a new endpoint you have the camera do slash app come down to API I'm gonna come down to uh create a new file called course okay and for this course I'm gonna then create another folder called create chapters and finally I'm gonna do a root dot TS okay so because of the structure of this app router this will actually map to slash API slash course slash create chapters so this is the path we are on right now so when we want to hit this endpoint we'll be hitting this endpoint which will run the end point within this file so let us do that first so we're going to export a function export async function for post it's gonna have a request which is a request type respond as the response type response okay so we're gonna just just return next response dot Json s hello so let's try out this endpoint I'm going to open up my API client okay so we're gonna try to hit this endpoint uh let me try to hit this endpoint let's say I can move this thing up here okay so we have this up here uh is my okay so right here we can see this is this such API says Call sasquerade Chapters right so if I come up here we can see that yeah if I just map it to slash API says course slash create chapters if I do a send a post request send I can see that hello world is being returned so you can see that the endpoint is working fine and so basically it maps to a post request to this endpoint so therefore we export this post function so if you want to export on get function so you just be the same export async function get then whatever function within this get will be triggered when you do a get request instead of a post request so now that we have that we can actually start working on the functionality of the AI so it's going to be a quite heavy endpoint but I hope that we can do it together so the first step is to uh let us just get the bossy body from it so let me put everything in a try cash block and we'll just do Khan's body clone's body request body equals to await request.json okay and how do we make sure that we got the end-to-end type safety remove the song in these validators where this create chapter schema well we can actually import that so let me import the uh create course schema from the validators and then I can just do cons equals to create chapter schema dot parse body so if you do this pause body we can actually destructure the title and the units from it with this typescript so in this case where the body does not conform to this schema will actually catch the error right and then within this error we can check if error instance of zot error right so if it's a short error that means uh the error it does not conform to the schema that we have given we can just return a next response uh new next response we can say invalid a body then passing the status of 5 400 for the invalid request body okay so if we have gotten to this line right that means these parts work that means we are sure we are guaranteed that this title and units exist within our request body okay so then what do we do what can we do with this request body the first thing here is let us just uh create the output unit type so let me give you the the shape of the idea the shape so we'll create a new type called output unit right within this is each unit it's gonna have a string it's going to have a title right so if you come back to the x-cali draw uh let me just refresh this page make sure that we can see what we are working with each unit has a title and within each unit we have multiple chapters so these chapters uh is going to be a array of chapters so each chapter will have a YouTube search query which is going to be a string and also a chapter title which is also going to be a string so save it I guess actually just not loading but it's fine so if you come back to our schema dot Prisma right we can see each course will have multiple units and each unit will have multiple chapters therefore we can just see this type here so these units will be uh so this is one unit so each unit will have a title and multiple chapters underneath the unit okay so how do you actually get these output units so here's where we talk about the the first AI part of this solution so we'll be using open AIS gbt AI for the text generation right however the issue with uh this kind of last language model is that it cannot actually properly produce Json valid Json most of the time sometimes it will generate Json that's invalid so maybe has a missing character within it it causes the entire Json string to be invalid so whenever we try to parse Json it will break the entire program therefore I actually found a really nice function online I'll I'll give credits to him and also link this Library down in the description below right so this Library basically is a python library that basically wraps the GPD API such that we can uh then create we can give the API the our ideal shape of the Json and the GPD API will then be able to uh produce that correct Json so how it works is that we ask gbd to Output Json and then if the Json does not match our ideal shape and does not match the ideal Json format we're gonna basically take the error and we'll feed it back into GPT and ask it to generate again so keep asking you to generate and generate again until it has given us the correct proper valid Json okay so actually we have to come down to our directory and create the the function to actually generate the valid Json from the AI so I'm going to come down to my uh lip Okay I'm going to create a gp.ts file okay so this gbd.ts file is going to create the function all right I'm going to copy in this function so this function of DV has a page bin within the description so you don't have to worry about it so only you do is just mpm install open AI so let me just walk you through what this function actually does so this function is called strict output it takes in the system prompt the user prompt and the output format so this output format is the shape of the Json that we want it to produce right so basically all it does is we can see that it will try uh for a certain amount of times so by default it will try three times so for the three times you will try to basically create a tech completion right then we will basically try to check if the Json is valid so if the Json is valid right then it will just return us with the Json however if the Json is not valid you will then try again so you'll come back and look back up here and you'll try again and passing in the previous error into the gbt so with this function I'll teach you how to use it you don't have to know exactly how it works you just need to know how to use it and that's what I'm here to do I'll walk you through how to use this function to generate valid Json okay so the first thing here is let's just see why this is not working do you mean open AI so let me just uh just save this file I'm gonna restart my typescript server so in order to begin using this we need to open the AI API key okay so actually just why is this not working open AI why is he saying that it's not working anymore do you mean to use import configuration from open AI uh so I suspect that that might actually be a new version of openai from my previous demo project so let us go into a package.json so open AI is using ah I think it's because it is a new version 4.0 by one so we actually don't want that we want it to have open AI at the version uh we want the version to be 3.3.0 right so let us try that so we'll downgrade open AI to 3.3.0 and then now it works again so it's just a version incompatibility but just make sure that you're on the same version as what I'm using here in this project so to get this open air API key let's come down to the browser let's go to open AI the website okay so uh what you need to do is you need to log in so if you don't have open AI account you just have to create a new account login and then basically over here I will give you 18 free credits I think if you come down to the API page we can come down to your your menu view API keys and let's create a new secret key I'm gonna name it learning this journey there's YouTube okay so create a key and then copy this uh API token so copy it we can close it out okay we close this tab also let us actually come now to our EnV file and let us just do open AI Dash API key then we'll just pasting the key that you have copied save it and now you have login in so now we're able to use this open AI function to generate the the proper chapters okay so let's close this down and let us get back to the uh the root here okay so the first thing that we need to do is uh let us come down to this create chapter so let's do the prompt engineering so how we actually use this trick output function is I'm going to declare the function called output output units right it's gonna have the type of output units right this comes from this type up here right so what do we actually want to do we want to do equals to a weight a strict output so this three output comes from the GPT function that we copy in just now so how this works is that first we have the system prompt this system prompt basically tells uh perhaps the AI on what is row is so we tell you that you are an AI capable of curating course content coming up with relevant relevant uh chapter titles and finding relevant relevant YouTube videos uh for each chapter so this prompting the AI on what Israel is supposed to do okay and then now we want to do we're passing the units we're passing the second argument so the second argument is the user prompt okay so how it works is that basically you want to create uh we want to create a bunch of units right so let's say this unit is going to be an array remember when we pass in uh these three units so if we pass in three units we actually want them to generate we want the AR to generate three units of content so what we can do is we pass in an array a new array of units.length so this guy have a array of three and then we'll fill it with this prompt will pass it as is your job to create a course create a course about uh title so this title comes from this title so it's whatever it passed into this first unit input here right and then we say okay the user has requested to create chapters for each of the units okay then then for each chapter provide a detailed YouTube search query that can be used to find and informative educational video for each chapter okay and let's continue the problem each query should give and educational information course in YouTube so let's save it right so this shows a lot of problem engineering so you can just copy it from the repo down below so after that the third argument here is the actual output format so what do we do want the format to be so the Json is going to have uh it's gonna be an array of this shape so the shape is going to have the title so how this function works is we pass in this so we are guaranteed that the output Json will have the key of title and this will be the description of what this keyword produce so the description will just be title of the unit right and then uh for the chapters uh it's gonna be we describe it to the GPT we tell it that it's going to be an array of chapters and each chapter should have a YouTube search query and a chapter title key in the Json object save it okay so let me just run you through what this function does again so because we are looking for an array of chapters right So eventually what we want to Output is uh an array of units right where each unit has its own array of chapters so therefore we fill it up with three so if the unit there's three units that the user has passed in we can create three prompts each prompt will be the same problem right but then basically it's going to create three of the same Json array and then for each of the unit we'll pass in uh an array of chapters right so then after this we can we are guaranteed that the Json output will have the correct shape that we want so let me just console.lock out the output units right I can also return the next response dot Json output units so save the file and let's just try to check the employee and we'll see how it works stronger come down to my uh my API and I'm gonna pass in a few things so we need what do we need we need the Titan unit right so for this I'm gonna pass in a Json object so the title is going to be about cow killers the unit uh is it units yeah so we need the units it's gonna be an array so this let's say we have the array of differ algebra and also differentiation so let's send a request in and then we can see that right now it's gonna be trying to generate the Json output so it's running this function and let's wait for it to actually run so it'll take a while because now it's trying to hit the AI endpoint the GPD endpoint and now we can see that it works perfectly so let's look at what Yes actually traded for us we can see that we're returning this output units right these output units we can see there is an array this array contains a a few objects in this case it contains uh four objects each object is a one unit right so we have unit one unit two unit three and unit four within each unit we can see that it contains chapters each of this chapter is a array and this array will have three chapters within it so and each chapter will have YouTube search query and the chapter title so now that we have this YouTube search query and this chapter title we can actually hit the YouTube API to then get back the actual YouTube a video ID okay so one more thing I'm gonna work on is uh let me think can we do okay sure so let us create the unsplash API first so other than creating these output units we can also we also want to get a a stock image so that we can display in the gallery so I'm going to come up to the unsplash so we go to unsplash API search for unsplash API click on the first link so it will probably you to the API documentation okay so let's try to get the API key so the first thing here is let us just go to the go to your apps and just create a new application so you have to log in and sign up as unexpressed developer first so just click and just um yeah just check off so that you accept all the terms so just name it learning Dash Journey that's YouTube create application just give you a description like uh AI course generator then create application and we need the API key so all you do here is come down to the request and here we are able to get the access key okay so I'm going to just come down to the okay we need this secret key so copy the secret key copy the clipboard come down to the EnV file okay and let us uh create the unsplash uh API key paste it in save it I will close it close on the tab okay so I'm gonna come now here to my lib folder under my lip folder I'm going to create another file called unsplash Dot yes so this file is gonna only export one function one very simple function Xbox cons uh get and splash image it can be async function that accepts a query of a string it's gonna basically use the and splash API to search for a relevant image so let's get the image response uh raw so it's going to be the raw response we'll do a weight um let's just import actions from exos which we do not have installed right so just npm install axials first so once we have access let's do a weight extures dot uh get all right so what do we actually want to get let me just copy in uh this link that I have here so let's save it uh so this should be impact ticks so I'll leave this into the comment the Box the sorry the description box for this uh link so basically what it does is we're hitting the api.unsplash.com search slash photos and we will just want one image per page right and we're passing the query as the query that's being passed into this function and we're passing the client ID this client ID will be the unsplash API key that comes from the environmental variable here okay so now that we got this uh response that we can actually restructure the uh data up here so then we can just do a return data dot results index 0 dot e dot urls.small S3 right so I'm just player playing around and inspecting the results I found that the image is stored into this uh results URLs or small S3 you can try to control the logging out the results and just viewing what is shown here but basically if you're passing this query it will be to get a relevant image so that's it for the unsplash API so let's go back to the create chapters so now let's actually get the image search term right so for the image search term we are given this title right how do we actually get the image from unsplash let's just do another AI prompt let us do cons image prompt uh wait sorry so let's just do image search term equals to a weight strict output so we're using gbg API again this time we're gonna say that you are an AI capable of finding the most relevant image for a course okay it'll pass in the user prompt which will say that please provide a good image search term for the title of a course about the title so let me just convert this into a back ticks so you can put this in okay and then we'll tell you that this search term right research term will be fed into the unsplashed API so make sure it is a good search term that will return good results okay so then after the user search term we can pass in the desired output a shape so we just want it to have returned us with a image search term a key right and then we tell it that all we want is just to have the a good search term for the title of the course so then now here we are guaranteed that whatever this returns will have a key of this image search term and we can actually even return this let us output units let us also let us also show you the image search term and image image search term let's save it okay and let's go back to our here and let's send this request again so other than just getting out the chapters and the units is also going to give us an image search term so with this image search term we can now hit the unsplash API so let's wait for you to generate and now we can see that the output unit is the same we have saw it just now but now underneath here we can see a new image system and has given us a system of this calculus mathematics so we can actually feed these calculus mathematics into this unsplash API to get the relevant image so now we can actually do a cons course image equals to await get unsplash image right so this is from the unsplash.ts file we have what do you want to pass in we want to pass in image search term dot image search term right because we can see that within this image system that's this key here that contains the actual query so therefore this image search term has this key and so we actually want to pass in the image system key into this image so then we got this course image so let's just also console dot uh let us just also return this a course image so you're able to see what it returns so you'll hit this endpoint once more so that we're able to see that the image works fine okay so we're close to completing this part so let's just wait for it to generate we can see that everything's working fine oh 500 internal server error so let's look at the error here uh okay one mistake I've realized is that uh you actually have to instead of the secret key so just now we copy the secret key into this unsplash API key so my mistake is actually not the secret key but you actually need to copy the access key so I've copied the access key and placing the unsplash API key so that's all that I've changed and basically I fit the endpoint again and this time you can see that the output you need is the same right we get the image search term for calculus and lastly we can see this course image which is the link that was returned from this um from the unsplash function here so if you click on this image we can see that it brings us to a uh brings us to this image let us bring it up here let's see if you can see this image right here so yes download it into my computer so if you open the file we can see that I mean it's just pixelated but uh this is the correct image to show the relevant search term so now that we have got this image now we can actually create a new row within the database so let me come now back to the roots so we are still in the slash create uh course page so after having gotten the output units image system and course image we can actually do um create a course equals to a weight uh Prisma Prisma will import from DB Prisma dot course dot create right so we create a new course we pass in the data so the name of the course will be the title from the title which is passed in from the request body up here okay and then uh for the image we can just pass in the course image right here okay that's good so now we have created this course we can create the different units and chapters so with the structure for cons unit of output units right so we can see that if you come back to this unit so each output you need is an object so this object contains title and the chapters okay so let's create the units so we do uh we can just do cons title equals to unit dot title right unit dot title right so these output units is it's not array type so this output units will actually have to be an array here output you need is going to be array so you have to append this part here to make sure it's an array and then you can just be I'm sure that it's gonna be have a title and then we'll so we have this title now then we can actually create the Prisma unit so we can just do Prisma unit it goes to await Prisma dot unit dot create we're passing the data so it has a name of the title right uh and then we'll have the course ID which is the course the ID created up here so once we've created the Prisma unit we can actually Loop through each chapter so we can now see that with each chapter we can take this chapter and create the relevant Prisma database also we can do uh let's just do create many right uh we can pass in the data you need.chapters.map right so for each chapter so for each I don't want to in turn I'm going to return the sorry we turn the name the name will be chapter dot chapter title the YouTube search query so we can see that YouTube subscribe chapter dot YouTube search query and lastly the unit ID will be Prisma unit dot ID so we're just creating a nest of creation so after all of this done we will have successfully created all the relevant units within the chapters and the units within the database and creating the rows okay and then I think that's pretty much it so we can just return a next response of Json let us just return the course ID to be course dot ID okay so we will be using this to redirect them to the page after creating this so I'm going to save the file and then I'm gonna come now here I'm gonna basically uh click Send but before that I'm going to open up the terminal here and right here I want to demonstrate that the the rows are being created so I'm gonna just do MPX Prisma Studio right so everything is playing now so let's hit the end point and so hopefully everything goes well uh the Gen the units and the chapters will be generated and you'll be added to the Prisma database right here so let's just wait for a while so it's going to try to create the chapters and the units and the courses okay and now we can see that 200 okay so he has created the courses and returned us with the course ID so if you now refresh the page we can see that a new course has been created so the name of calculus has been created for us the image has been shown and we can see that the units the three units has been generated so if you come down to the u3 units we can see the names have been generated and it belongs to a certain course if you come down to the the chapter we can see that it's running to all the chapters and he has basically shown us that we created the names the unit IDs and basically it belongs to the units and right now it doesn't have any questions okay so that's good so we have actually created all the the proper uh proper units and stuff okay so we're doing amazing so now that we have completed the endpoint and it has been added to the Prisma database so now actually link it to the front end so when we want to submit we submit and then we hit the endpoint and then we have to redirect them to the next page the next step of this entire thing okay so uh I'm gonna close down the file here I'm going to open up this create course form right so create a course form so what do you want to do when they submit okay so the other thing another package will be installing is we'll be using a react query so react query is a really nice way to handle mutations and basically anything that does with the heating of back-end endpoints so let's just first install react query by doing npm install react uh reacts quote we're not in react query but I think let's go to the 10 stack query page so that we can see installations on how to install it so it's a really nice library for State Management for the server side State Management so let us get started and then let's see how to install it uh let us just go to installation okay so in npm installed at 10 stack slash react Dash query okay so let's install it and then while we are installing that let us let me show you how to actually start up real query so once you've installed react query we need to go back to our providers file so remember the providers file where we exported the team provider okay so we actually need another provider for the react query so let's import a few things from uh react query so import from at 10 stack slash react query so we need two things the query Clan and so the query client provider client provider okay then let's wrap the entire thing with the query client provider okay then let's bring that all up here so this query client-up provider will require a client so this client will be from this query client variable here where if we instantiate this new query client let me pass in this query client so the reason why we need a query client provider wrapper for entire application is because real query does a lot of caching for us imagine we are on one page of the application and we fetch the same endpoint of data right and then we go to that page and we fetch the same endpoint again react query will notice that hey I actually just flashed this data just like a few seconds ago would it be a waste of resources if I actually fetch it again so reactor will actually catch the results from the previous uh endpoint Heating and then it will take the results and just return us without having to send another API request so this will actually save us some time and bandwidth and make it more efficient so therefore we need this react query client provider such that our entire application has access to the cache okay so I will understand the part so now that we have this query client provider we can actually create uh the the mutation function to hit the endpoint so a mutation is just basically any action that hits the end point that he's an end point okay so what we actually do here is uh for this create course form we can actually just do cons uh mutate right we actually destructed that from use mutation which comes from 10 stack chlorine so this used mutation takes in a few things the object will take in uh the first thing will be mutation function so this is actually what does the actual mutation which is the endpoint Heating so these uh mutation will take in or taking the input so this input will uh from this input variable that we have here so this input will have the title in the units so we can actually destructure them the title and the units so what you actually do we want to do take the con response a response it goes to a weight axios dot post slash API slash course slash create chapters so remember this endpoint it comes from this slash API call slash create chapters this root so it's actually what creates all the units for us okay so after we hit this endpoint we just want to return response dot data so okay and then uh for this mutate we can actually rename it let's rename it to create chapters we can also destructure the East loading state so rare query also provides us with another utility so that when the query is being sent out and is waiting for the response this is loading state will tell us uh whether the query is still being loaded okay so I'll come down here for this button for this let's go button I want to disable it when it's loading uh it's loading okay so when I submit this uh form right for this summation here I will actually want to hit this endpoint so when I submit no I want to call this create chapters so this script to create chapters is this mutation function so when we call it it will actually hit this endpoint here so what do we actually pass it in we want to pass in uh we pass in the data so right now here what we actually get this data we got this data right so these create chapters requires title units so we can actually just destructure this type data right destructure the data in terms of this object right uh actually we can just pass in this data yeah so because this data will have the same input type of this input right we can just pass in the data and then for the second argument it provides us with some nice features like on success so when the thing is successful it's gonna call its function when the endpoint is a chosen error it will call this on error function so if there's an error I just want to just um I'm gonna just do console.error error okay okay so remember what happens is uh I'm gonna add one more check up here right so if the other units are blank so if one of the units are blank we actually want to throw an error telling them that they should fill up all the units so I can add one more check above I can say that if data the units dot sum right unit right if the unit equals to empty string right that means that they didn't fill up all the units completely I want to provide them with a toast right so right here I can just let me close this up this Learning Journey we can see that like let's say if they submitted let's say they press let's go right on this form this tools too busy tell them to submit uh to fill out all the fields okay so uh let me just record back to the create class page so how do we get that toast so this toast will come from has this toast so we can do MPX uh set CN and UI at at toast so you add the toast uh thing for us let's install the dependencies so we have to come into layout.tsx right so under the layout.tsx let us import the toaster So Below the children let us import the toaster component from UI and I will save it and then in here we can actually uh get the toast so let us just get the toast function from so we can just uh sorry let's just do cons toast equals to use toast so this used toast come from the chassis and UI and then here we can do so basically if they haven't fill out the units we can just do toes the passing the title of uh let's say error description of please fill all the units and we have a variant of destructive we'll save it and then let's try let's try submitting so we can see that if any one of them is empty now you can see that uh it will just not submit okay and then also want to return from here do early return okay so we can see now it returns so unless we fill up everything it will return so if everything is filled up then it will hit this create chapters function pass in the data then you call this function with the title in the units and then it will just return us with responsible data so here in the the actual post request we actually also pass in the title and the units okay save it so now everything is working great and let us for this uh error if there's any error let us also just do a toast yeah and then we'll just tell them that something went wrong on the back end okay so remember what happens is whenever we hit this endpoint create chapters right create uh create the chapters everything succeeds we can see that it returns us with a course ID right so what I want to do is I'm gonna after they have submitted if it success if it's if it succeeds uh on success we actually want to destructure the course ID from it course ID right so if you're successful we are guaranteed there's a course ID that is returned from this course ID field here so I'm gonna then basically route them to a new route so to basically route them we're gonna destructure the we're gonna get the router from use router so this view shelter will come from next slash navigation be careful to import it from the correct navigation instead of the router next slide shelter is for the Pages directory next slash navigation will be for the Nexus 13.4 app directory I'm going to uh basically call this hook and then underneath here I'm just gonna do on success I'm gonna just uh give your toast I'm gonna say title of success and then cross created successfully so after the toast I'm just gonna router.push so I'm gonna push them to a new route create search course ID okay so now let's actually create this route to make sure everything is working so I'll just come down to this Explorer so to create a new route with this uh slug right with this Dynamic slug we'll come down to app we'll create a new file uh right now underneath this course uh uh on this app we have this create right so this create a map to slash create but what if we have this slash create slash course ID so we can actually come down to this uh create page and we can actually add another slug to it so to do that let us just add one more file under this create we do uh in Brackets course ID and then we create a page.tsx so here we'll have access to the uh this course ID so let me just do create chapters we'll save it okay so let's just make sure everything is working now for first create class form okay so let's try actually hitting the endpoint so I'm gonna just do let me just do how to make clothes we'll do pens and shirts remove the unit and let us hit the endpoint so because it's sitting at endpoint you will disable the button right because it's loading and hopefully if everything goes well it's gonna return us with the course ID and you'll navigate us to the correct page where we can move on to the next step okay so successfully so they can see the course is created successfully we are navigated to this endpoint right we can see that we can see that the ID has been appended to the course we have brought to be have been brought to slash create slash this slot and we have also gotten to this create chapters a page okay so all of this costs and units are also saved to the databases so the next step here is we create the second page so if we actually come down here to the original original picture let me just show you what is next so I'm gonna just copy this m ID to demonstrate it for you so if you come down to attack slash create slash this so this is the page we'll be working on right now so like look okay so it's this part here so after the creation of the course we actually want to actually confirm everything and let them press the generate button and with this generate button will then actually go and hit the YouTube API and generate all the questions for the individual chapters okay so how do we actually get the course uh the course idea of the slot right because right now we have the slot that's on the URL but we cannot access it from this create chapters page okay so to get the course ID we can actually pass it as props so next yes does the hard Lifting for us if there are any props we can just take it from the programs right the course ID off string so this course ID will map I will map to the whatever name is within the angle 30 square brackets so if you name it anything this must match okay so you can destructure within the params we can disrupture the programs you can get access to course ID and let us just display the course ID out here so then we can now see that this course ID maps to others in the URL okay that's perfect so now we have this let us continue working on it so the first thing here is let's check that the user is logged in so if the user is not logged in let us just redirect them back to the gallery so do cons session equals to await gets all session Okay so we've done this before so we make this into a server component and then we do if not section dot user uh dot user oops not user return redirect to slash gallery okay so uh let us actually get the course so cons course equals to await a Prisma dot course dot find dot find unique right so we have this ID right we can find the course with that particular ID where the ID equals to the course ID okay so if there isn't any course right if the course doesn't exists we just return the redirect to slash create okay so that's good that's good and lastly is uh for this course right uh we also want to include the units right and within the units we want to include the chapters so basically this this is Christmas way of doing joints so traditional SQL joints is because we have three tables across the units and the chapters right if we do this include you basically get us back all the joints and we'll have the entire Json tree structures including all the courses units and the chapters and we can basically show that by just returning a pre and I can show it as Json or stringify course now and two so let's just see why it returns okay so this was being returned from the desire base we can see that we got the course we got the image of the course we've gotten the individual units right and we've got the within the individual units we also got the individual chapters so let me show you what happens if we do not show the include so if you do not have this include right I'm gonna comment this out let's save it we can see that it doesn't include the units right and let's say if I include the units but I don't include the chapters we can see that it represents the units but it doesn't give us the chapters within the units so if lastly if I include the chapters then you return us with the chapters So This Is Christmas way of doing the joints I hope you understand okay so let's just do the Json the the front end UI for this so we will need to look uh similar to like this right with all this uh with all these things so let's just build this UI for this top part first so if I want to return we can return a div uh return on diff class name of flex Flex just call items start Max width of XL MX Auto and margin y of 16. okay uh let me just move this down here within the div let's have a H5 with a class name of text small upper case text secondary dash for ground divided by 60 so 60 of the opacity and we can actually pass in the course name and then underneath that we can actually pass in the H1 that actually contains the cores dot name so this H1 has a class of text 5xl and font Port so let's save it and now we can see that we have this nice how to make code thing here okay looking good So Below the H1 right let's create the info card here right so similar to what we've did before so I'm gonna create the div so it's gonna be the card has a classroom of flex padding of four margin to a five border of none and background secondary and we'll have an info icon from Lucid react class name of with with 12 height 12 material of 3 and text blue or 400. then beside it below it we'll have a div that contains the information so we generated chapters for each of your units look look over them and then click the uh button to confirm and continue let's say that and we have this nice info card here that looks the same here okay that's good and lastly here we have this separate this entire thing here this entire form will be a separate component so I'm gonna come down here I'm gonna just create a new component called confirm chapters.tsx TS RF SC save it let's come down here so just before the closing diff we'll just do confirm chapters save it and we can see now that the component shows up here so that's nice all right so we're gonna create this confirm chapters component right here so we can basically look through each of the units and the chapters and create each of these bars here so we'll start with the UI so we're gonna go into this confirm chapters application uh this component so the first thing here is obviously we need access to the course right we need to access the course and all the new units so we get it from the props so I believe what I want to do is uh when we are in the slash let's say course ID when we have the conference I just want to pass in the course here right so this course is from Prisma from here right I want to pass in it as props so let's receive it in here so the course the course is gonna have read the course from Prisma client and because we're gonna end it with uh the units right the units is gonna be the unit from Prisma client and we're gonna basically join it with chapters and this is gonna be a chapter from prismaclan could be an array and it's going to be also an array so now everything the type matches right then we can access the course prop here so these calls will contain all the units and the chapters and let's now actually display the UI so for the div here let's return uh div class name of with a full and uh margin top of four okay and basically when the map to each unit right so we have uh unit one in two unit trip so let's map through the units so we'll do course dot units dot map so we have the unit and the unit Index right let us display the so let's return a div uh with a key of units.id right so we have a class name of margin top of five within that div let's have a H2 that showcases the units right so we have units unit index plus one because it's zero index space the cluster will be tax SM upper case text secondary second Ary Dash foreground 60 let's save that first so we get c unit one two three four So within the units we want to display all these all these uh names and the actual uh chapters so I need the H2 let's display the H3 of the unit dot name so the history will give you a class of text to Excel and fonts of both let's save it so we will see this here okay that's good so now that we have that let's actually Loop through the chapters and display that so below the H3 let's have a div okay and then we have a class name of margin top of three within the div let's map to the units so the chapters the unit dot chapters .map so we have the chapter and the chapter index right let us return basically another component uh let's see here what's the issue here so I think this should be here right let me just redo this so you need.chapters.map to chapter and the chapter index right okay okay so it works out so let's create another component for the individual chapter card so each card is like this green box here okay so let's create a component so let me just check something okay uh go under the components and let's create this chapter card.tsx tsrafce okay so I'm gonna save this first so we're gonna return just basically a chapter card as so okay uh that pass in the key which is the chapter the ID and we'll save it first so right now we got the chapter card stroke here so how do we actually display the nice names okay so for the chapter card we obviously want to pass in the chapter into here right from this chapter we also would like to pass in the chapter index so let's create the props to receive these two things so chapter card for the props we have the chapter which is which is just a chapter from Prisma Clan and chapter index is just a number okay so now we can destructure the chapter and chapter index okay so for the chapter uh for the chapter uh card here let's display the UI okay so for the UI it's gonna have a diff okay uh let's give it a key of chapter the ID uh then I'm gonna just within the div we have a H5 that just shows the chapter chapter index plus one uh another chapter name and then let's save it uh undefined chapter dot name so of course you save the file here also so that everything is okay that's good so now we got this little things let's also give it one additional styling so for this div here let's give you a class name and I'm gonna use the CN utility so click on import CN from our utils I'm just gonna pass in a BG of secondary okay so later on we'll have another state that talks about whether it's successful or not so we can have a state like cons uh success and set success is false so if you make this into a client component first right we'll save that here okay so I'm gonna give you a bit more styling so uh before here I'm gonna add uh the base Styles so gonna have a px of four particular of two margin of 2 rounded uh flex and justify between let's save that so now it looks a little nicer so later on depending on this uh success very success State here we're gonna display uh different colors for the card okay success so the success State can either be a Boolean or or a now value okay so at first it's gonna be now Okay so when it's now it's gonna have this gray color Okay so we're gonna apply this background secondary if it is if the success equals to now we'll then apply a big background of a BG rate 500 if the success equals to false and then lastly uh right here the reason why is throwing an error is uh let's see oh okay so this has to be wrapped in an object for this to work okay and lastly we'll show a background of green 500 oh here if success equals to true so we save that so now depending on the state so let's say is true right everything will be green Okay then if success is false so in case there's any error in the back end we'll set it to false so in the beginning it's going to be now so it's gonna have this gray color state okay that's good so now let us continue with the the UI here so let me just add uh these two buttons okay so underneath the content confirm characters right under the confirm factors let's add to two buttons uh so just below the closing div So Below the or just before the closing diff let us add another diff so this div is for the buttons class name of fax items Dash Center justify Center and margin number four let us have a separator separator from UI class name of flex of one okay so underneath the separator we have another div with the class name of flex items Dash items their Center margin x04 okay so within the div let us have a uh two links right so each button is a link where we can either go back or we can generate okay so we have a link uh from next slash link so we'll just the first thing is gonna be to slash create and we say go back so the class name is gonna be uh just a button variance button variance so this is basically we can make the link look at a button right so if we go back here we can see right now with this back button but it's actually a link in Disguise okay so we're passing another thing uh we can also within the button variant we can tell you that we want it to be a secondary variant so now you look just a little nicer here okay so below the link right so uh inside the link above the back we want to add a Chevron a left icon from Lucid react let's give you a class name of with 4. 4 material of two and we have the stroke wheels off let's say four so just get that nice icon back here okay that's good so then below that link before after the back button we have another link similar to this right this time we have to uh actually it's not a link is a button so the button will be responsible for the generation so let's import the button and let's just say generate Okay so this is obviously gonna have a type of button okay so this is where we actually trigger the the backend API so uh I'm gonna give you the cast name of just sorry uh not here type button class name of margin level 4 and phones semi bold okay that's good that's good and then just below the button uh be below the button below the div also we have a separator costume of flex dash one save it and right now we have this nice format so it looks the same here okay so uh this generate here let's add the icon share share run right icon class name of with just four height Dash four and margin level 2 and the stroke width of stroke shock width off four let's save it okay that's good so now uh when we click on this button what do we actually need to do right we don't need to start generating right so on click we want to call a function that creates the generation so what I'm going to do here is I'm going to set up the back end first so right now we can see that we need it to be a client component right so let me just uh go up here and tell it to use client so it's a client direct fifth diff okay so now everything works so how do I actually uh do the back end uh so let's create a new route so let me just look at how might we want to set this up create okay here's what here's what I'm gonna do I'm gonna come down to Alpha so under slash API okay I'm going to create a folder called um core uh chapter so because we are trying to get the information for the individual chapters right so I'm gonna come down here to chapter and then we're gonna create a folder called gets uh info info and then inside here I'll do root.ts so this API will be mapped to slash API says get access chapter so let's get info yeah so because we are in the slash API chapter then we have this Photograph get info so these maps to this road so what I'm gonna do is um I'm gonna just export a normal function export async function called post is eager taking a request object with tough request request response as as response and I'm gonna do is I'm gonna fake uh some something so I'm gonna do a weight uh I'm gonna have a fake function called con sleep it's gonna be a function called uh to return a new promise we take in the resolve and I'm gonna just set timeout to resolve it in the math.random times 1000 okay times four thousand so what this does is uh sorry let me just close this okay so this sleep function will basically wait uh wait for random way for random random timing between 0 and 4 seconds right so all I'm gonna do here is I'm just gonna just do await sleep gonna call the function uh here is a callable because return is gonna be a function using function that returns this okay gonna cost it so these are for demonstration you have to code this out yet I just want to just return a new uh next response dot Json import this Json message hello okay so I'm gonna just demonstrate what happens so because we have this slash epsf chapter info I'm gonna try to hit this endpoint so slash API says chapter says get info so if I send a post request it's gonna wait for between one and four seconds so in this case it waited for 2.26 seconds so we can send it again it's gonna wait for okay this side is 2.1 seconds and this time it's gonna wait for uh three seconds so it's gonna just wait for random timing between 0 and 4 seconds and return so I just wanted to have a skeleton API because what we're gonna do now is I want to show you the front end the front end way of just doing this asynchronous function of calling each one individually so if you come back to the chapter card coming to the chapter card what I'm gonna do is we'll have a mutation function so cons mutate mutate it goes to use mutation right the mutation function is going to be async function what does it do it will do cost response equals await uh await axials.post let's import axials slash API chapter so let's get info Okay so with this info and then we got a return response dot data so this uh Network request is going to block for random timing with between 0 and 4 seconds okay so with this blocking we can simulate the actual work being done because right now I just want to show you how to build the front end okay so after that after we have this I'm gonna just rename it rename this mutate function to get chapter info and we'll have a loading State it's loading okay so that's good what I'm gonna do now here is uh what I'm gonna do now here is I'm Gonna Save it okay so when I click on this generate button I want all of this individual uh cards to call this get chapter info individually so when I click on this generate button this card will call get chapter info discount code chapter 24 this card will call chapter 24 all of this asynchronously so it will send out parallel requests to our backend servers and then therefore we can create that asynchronicity instead of basically calling the chapters one by one right so here I'm just going to remove this chapter index plus one because I see that there's some duplicates here okay so uh that is good remove this chapter okay so in order to do this generic function right we have to actually do I'm going to show you something really cool using the use ref hook so first thing first let us come back to our uh create chapters here okay I'll not create chapters uh what is this function called it was what is this thing called let me see a chapter card will come down here I named it to be core slash course ID page of TSX confirm chapters okay that's good so come back to the confirmed chapters uh component right what I'm going to do here is I'm gonna store a list of refs so a react graph is just a reference to each individual component so I'm going to have an array of refs so each ref will basically point to each component then when I click on this button I'm gonna basically call the function on each of the ref telling each of each card to actually call the function so to do that I'm gonna have a variable called chapter ref right this is gonna be an object right so this object is gonna be a record between a string and the react dot ref object and then let us just pass it in here we initialize it to be playing dictionary okay so what this does is that this chapter ref is gonna be an object let me just comment this out for you so it can better see what we're doing so this object I imagine it to be something like uh actually let me just do this so it's gonna be let's say ID of the chapter 190 so because each either chapter has an ID itself right so we'll have the ID let's say uh chapter one's ID it's gonna map to a react ref Okay so then we have chapter two will be mapped to another react ref okay so then with all this ref right we can actually call the function within it so let's just create this ref object first so with this ref object right uh we can actually put it in here so what I'm gonna do is down here I'm gonna do uh for the course dot units so each course has a bunch of units uh right then for each of the units for each unit we want to do Loop through each chapters right because we're trying to bind the chapter ID to the the ref here so bind the ID of the chapter to each individual card the component that we have here right so you need the chapters uh dot for each so for each chapter what do I want to do we want to index into chapter refs run the index into chapter ID and we want to set it to a new react.use ref okay so uh initiative to now so right now we can see that uh ESC is telling us that u-sraft cannot be caught inside callback right but the thing is uh although this is not good practice but it's actually a method that works right so all you do is just just put es lint disable next uh next line okay and then we will react Dash hooks says rule off hooks we do this let's save this so uh okay so all we need to do is put eslint disable next sign and this won't work so let us just try console.logging out the uh chapter wraps so able to see what's happening here so coming to console we can see that is an object containing each individual chapter's ID so we have one two three four five six seven eight nine tenure and 12 chapters so is individual chapters and each of this chapter is mapped to each react ref right so now all we need to do is pass this ref pass the individual ref objects into this chapter card into this chapter card so we're going to pass in the ref here to the chapter refs at index into the chapter dot ID okay so right now it's gonna throw an error because you cannot pass a ref into a custom component so what we have to do is come down into our chapter card and we have to wrap this entire component into a react or forward ref this is how you pass refs into components so you have to wrap this entire thing into react.forward ref right I'm gonna pass in the generic and I'm gonna wrap this entire function into this now formatted so right now it's telling us that uh it's missing a display name so we need to fix that by coming down here and just doing chapter uh card dot display name equals to chapter card so this will fix it and right now this thing here is a string error because we need two things we need a export type of chapter card Handler so let me just write out the code and then I'll explain what is happening later okay so we need a function called trigger load it's gonna be an empty function that returns void so this thing here is going to take in two things chapter card Handler and props okay so let me explain what we just did here so for this chapter card is a component so we're telling you that because we want to pass in a ref object from here right we're gonna wrap this entire thing into a forward ref so we're able to receive the ref okay the ref will then take in two generics the actual Handler and the prop so this props is what's being passed in here so we can just uh remove this and it still works right but then the first argument of this we can actually receive uh the props and the second part here we receive the ref okay so Within These props we can actually destructure the chapter and chapter index so let me just route you to one more time this react forward ref will accept this function which is our component the first component will be the props which is passing here and the second combination is the actual ref that we have in here so we have this array of rest will be passed into this ref object here okay and then now we can actually create a a function within this we can call react.use imperative Handler handle we're passing the ref object and this ref object this thing will return a function the second argument will be function right this is where we actually Define the trigger load function okay so we do async trigger uh trigger load right uh this figure load uh actually this is it returns an object so like this and so this object this trigger load is gonna have just console.lock hello okay so what we did here is we defined this function right and we have rebounded to this uh ref so we're saying that if you come around to this chapter ref ID this component now has a function built into it called trigger load so how do we actually call this trigger load function so if you come back to our confirmed chapters component we scroll all the way up here so this ref object actually requires one more thing here which is the chapter card Handler which we export from this chapter card so it's telling that this array of refs each individual ref will have this trigger load function okay so if we save it and then let's actually now console.log console.log chapter refs so let's go into the console we will see now that all of these individual IDs are bound to bound to a component and inside this component does this trigger load function so how do you actually trigger uh that function call all you need to do is whenever we click on this generate button right all we need to do is we can just uh we can look through the chapter ref right we can just do object of values uh values chapter refs so this will give us the individual refs so each ref what we can do is or for each graph can do ref.current right and now if you do a control space you can see that this trigger load function that we have defined in here the trigger load therefore we can just call the trigger load and this will call the function within each component here so let me try this out so I'm gonna just open my console and can just clear out so when I press generate you can see that 12 of Halo is console.lock meaning that individually each uh each component is calling this function so I hope you get you get the idea all we are doing is we're binding this trigger load function to each component then when we press on this generate We're looping to each a ref of this component and calling the internal trigger load function hello why not I just uh call this a mutate function so I can bring this uh use imperative and we'll just bring it down here so instead of calling the controller lock I'm gonna call the get chapter info okay then I can do the chapter info I'll pass in I undefined and then on the thing on the second thing I can call on success success okay so what happens is I'm gonna call this trigger load function individually on the components it can call this get chapter info which will pass as this mutate function right we renamed it so this get chapter info will actually call this endpoint slash EPS chapter get info right and because we have defined the get info uh to basically return a timeout of a randomly between two and four seconds so it's gonna stagger the response time so I'm gonna save it I'm gonna come back to our console and guess what happens when I press this generate watch carefully I'm going to press generate you can see that between 0 and 4 seconds is the link coming back randomly yeah so this is how we're gonna basically do the asynchronous city right so then now the next step here is to actually write the function uh write the function to actually get the info to get the individual chapter info so get chapter info chapter info here so now we can actually do the logic to basically hit the endpoint to get the YouTube uh video ID and get the transcript okay we are doing amazing so after we have done the imperative handle okay right now we want to basically for each chapter we want to get the YouTube video and also the transcript and generate the questions so within the root.ts So within the get info page right so if you come back to our chapter card right we're able to see that we're hitting this endpoint so what I want to do is I want to pass in the chapter ID itself right so that we know which chapter card we are trying to generate the info for so uh other than this I'm gonna pass in another thing here I'm gonna pass in the chapter sorry chapter ID right passing as chapter .id okay so how do you access this chapter ID within our root right let's go back to our root.ts I'm just gonna delete this sleep function because that was a fake fake data anyways right so I'm gonna just do cons body parser see the object right we want to have the chapter ID to be a z dot string okay and then now we can actually do the check so we do close body equals to a weight request of Json then we can destructure the chapter ID from body parser dot parse the body so we can see that typescript gives us a chapter ID which comes from this DOT type here so if this fails we can catch the error we gotta check first so um if the error instance of a z dot zot error right so this instance of this one word so if instead of error we just want to return next response dot Json we're gonna pass it the success or false so it failed right and then passing the error as invalid body return status of 400. okay then if it's not if it's not assault error we're just gonna return a generic next response dot Json the return success of false then we'll go toward the error is unknown because we don't know what happened what what went wrong and lastly the status of 500. okay that's good so now once we got to this point we ensure that chapter ID actually exists within the request body so what's our first step in getting the thing so let's set up the YouTube API first so what we're basically doing is we're gonna look at this chapter so remember that it's in the interview chapter right each individual chapter comes with a YouTube search query so basically for each chapter I gotta look at the users YouTube search query we're gonna search the YouTube API for that video so come back to here I'm gonna just search for the chapter first so course chapter equals to await Prisma dots chapter dot find unique uh we're gonna find it where the ID equals to chapter ID if a chapter doesn't exist we're gonna return our next response dot Json of success equals to success equals to false and the arrow B chapter not found and then pass in a data's off 404 not file okay let's save it that's good so once you have the chapter let us actually get the uh YouTube API so now let's set up the YouTube API first I'm Gonna Save it so to set our YouTube API come back to your browser go back to your Google Cloud console under your Learning Journey Dash YouTube Project and one basically search open the search and search for YouTube scroll all the way down until you see this YouTube data API V3 version 3 okay okay so with this V3 we're gonna basically enable the API so this uh API provides access to YouTube data such that we're able to search for videos or playlists Circle enable it so wait for a while and basically we need the API key so that we can use this API okay so once you have enabled it come down to credentials okay and let us just create a new credential create a new API key okay coming to your EnV file dot EnV so under the unsplash API key we have a YouTube underscore API key copy in the API key from Google Cloud console and then we can just close this page so this equals to this okay that's good so save it and we'll close it so now let's create a function to actually search up the YouTube API so come along to your lip slash lip create a new file called youtube.ts okay this youtube.ts will continue this function called search YouTube so export a export a async function called async function called search YouTube right this search YouTube is going to take in a search query which is a type of string so what we're gonna do is we're gonna just do search query equals to encode URI component so this will make sure the search query uh conforms to the URI uh format so if your search query is like Hello World right this will be converted into hello Plus World so this encode your eye component will just make sure the format is correct and then we can just do cons uh cons equals to await axials dot get right uh so what you want to get I want to get that YouTube API right so I'm gonna just copy this link here from my code so I'll leave this in the description as well but I just want you to watch this so we're hitting that endpoint so these are all from the YouTube documentation so Google apis slash YouTube V3 slash search so the keyword will be so the key the API key will be what we're passing Here YouTube underscore API key right make sure that whatever name here matches so YouTube underscore API key and we're passing the query to be the search query that we're looking for here and we want to search for videos that have the duration of medium and make sure the video is embeddable and also it's a type of video and the max result is just five okay so we have the response we can actually restructure the data from it and then we can just uh check if not data so if we do not find any search we can just control the log uh YouTube fill and just return now okay so we have not found any videos okay but if uh the data dot items index zero so uh equals undefined undefined we can also do the same so console.youtube fill here then if everything goes well we just want to return Json the items index 0 dot ID dot video ID uh not Json data so how I got all these data washes by inspecting whatever it is being returned from this okay so I basically got this uh the shape from just inspecting whatever is being written you can just console log it out and then view the video ID so basically what this does is it will this access YouTube function will return us with the video ID based on the relevant search query okay that's good so one more thing that I want to do is I want another function called a function called get transcript so this get transcript will accept a video ID right so we will then use this video ID to get the transcript of the entire video so to do this there's actually a library called uh YouTube transcript so npm install YouTube Dash transcript right so this Library will allow us to get the transcript of a video given the YouTube video ID so what we're going to do here is going to come down here and all we're going to do is put try cash block so we'll just get the transcript array be await YouTube uh transcript so let me just import this module up here so we'll do import uh import YouTube transcript from you from YouTube transcript okay so we're gonna await YouTube transcript uh dot fetch transcript we'll pass in the video ID and then for the options we'll pass in the language language of English and the country of uh en okay and then we'll just do like transcript equals to be an empty string first so we're gonna do just do four Let's uh T of transcript okay we're gonna do transcript plus equals to Trend t dot text plus uh empty space here T dot text yeah uh let T of transcript uh so what I need to do is transcript array not t of transcript but TF transcript array so this is individual transcript array will have an element so it's going to be an array so we just want to extract the text and we just want to append it like this and then after that we just want to after the follow-up just run to return transcript dot replace all right we want to replace the the new string the new line character with the empty space and then in the end if there's an error visual return empty string so maybe there's some error with getting the transcript then we'll just return empty string otherwise it will return a full transcript of the video ID okay so that's good so now let's get back into our get info page uh get in full root so after getting the chapter so now let's actually get the video ID right click on video ID equals to await you get a search YouTube for the chapter dot YouTube search query so this YouTube says Curry will be searched from the YouTube and then we can get the transcript so cons transcript equals 208 get transcript of the video ID okay then now let us actually get the summary okay so how do you get a summary let us use the a use the AI so we've done this before so we'll just import the Corner summary equals to await strict output so strict output so for the user for the AI we'll just tell it that you are an AI capable of summarizing a YouTube transcript okay and then for the user the user prompt will tell you that summarize uh summarize in 250 words or less and don't talk and sorry and do not talk of the sponsors or anything uh unrelated to the main topic Okay so we also don't want them to introduce what the summary is about also do not introduce what the summary is about so go save it that's good and then we just want to pass in the transcript right so we just append it to the end the the uh Slash n sorry at the end we can do slash n and passing the actual uh transcript here so basically pass in the context of the transcript and tell the AI to summarize it lastly for the shape of the output we just want the object of the summary we just tell it summary of the transcript we'll save it so now this what this function will do is it's going to return us with an object so this summary object will have will have this a summary key so we do summary with string so then we can extract out this summary object here so this summary will map to this summary and is what the AI will generate for us okay that's good so lastly let's see let us just return next response I just want to show you the output of this right so next response of let's see the video ID the transcript and the summary okay we'll save it and let's just try testing it out first so we know that this function this root will taking the chapter ID so what we can do is I'm gonna come into my Prisma Studio so we're in the chapter I'm going to take a normal and like a this chapter ID so introduction to limit right I'm gonna come down here uh so this HTTP colon such as localhost 3000 slash API chapter slash gets info so we'll pass in the tab chapter ID to be whatever we'll copy from Prisma so I'm gonna send so what it's gonna do is uh internal server error let's look at what the error is okay I didn't issue was because we were doing get requests now so just need to change it to a post request because right here we are passing we are exporting uh the roots to be post right therefore needs to be a post request so let's try sending it again so by right what it does is you look up the course you look up the YouTube search query then you'll use the function you have written to such a relevant video from YouTube and get a transcript and then feed the transcript into the gbd API to summarize it for us so let's wait for a while as it generates the the relevant information Okay so okay so that's good so we can see that we have gotten a valid video ID and then we've also gotten a transcript so it's a very very long transcript right it's got a very long transcript and the reason why right here we can see that there is no uh we can see that right here there is no summary is because uh basically because the transcript is too long and when we try to fit this information into Chef gbt it couldn't generate it out for us so what we can actually do is uh we can actually truncate the the transcript here so what I'm going to do is just for the transcript I'm gonna just do uh let's max length equals to 250 right and then I'm gonna do Trend so I'm gonna let let transcript so then transcript equals to transcript dot uh split so let's play on the empty word dot slice 0 to maxlang dot joint so this is this function this line will basically just split it by the words then limit it to 250 words by doing the slice and then we'll join it back with this string so we're just limited 250 words such that it the summary won't jump together so now let's try it again so this will just make our GPT um agent handle it better so that we don't like bombast it with like two main tokens and overwhelm it okay so we can see now that if we send the request out uh it will generate the you'll try to search the YouTube for the relevant search query and you get a transcript so we limit that transcript to do 50 words you can see now the summary is also working fine so the summary just summarizes what the transcript has for us okay so you can experiment with the with this number the max length so we can also experiment to with like let's say 500 Words let's see how this handles well let's see if GPD is able to handle 500 Words going in okay so in this case you can see it still works so the transcript is cut down to 500 Words and the summary still works okay so that's good so we can we we have success seen that the summary and everything works uh fine so all we need to do now is um let's continue on with the code so after we have generated the summary and the transcript let's generate the questions right so remember the questions has to be in McQ format so what we're going to do is uh I'm gonna come down here I'm gonna do cons uh questions equals to await get questions from transcript okay so this function here is gonna be from the let us just do it from this I'll say let's go under the youtube.ts file right and then we actually export our function call export async function get uh get questions from transcript so we're taking the transcript as a string right so we get the transcript from the string and we'll actually generate the questions okay so how do we actually do this so the first step here is uh it's gonna take in the transcript obviously and also uh should we you're taking the course title it's a string okay so we're gonna take in two things and we'll generate the questions so the first thing here is let us get the questions cause questions equals to await straight output so we want to use the GPD API again so the first thing here is this time the AI is going to be you are a helpful AI that is able to generate McQ questions um and answers okay the length of each answer should not be more than 15 words right and then so there's a system prompt then the user prompt is let us uh five questions so new array of five and we'll fill it up with this system pump you are to generate a random heart McQ question about the course title okay and then we tell it with context after following transcript then without passing the transcript to the API okay so the reason why we did new array of five is basically because when you generate five questions so if we do a new area of five the output of these questions will be a five an array of five objects and each object will have this shape so the shape will have the question which is just a question the answer is the answer with max length of 15 words okay so we also want the different options right so option one is gonna be option one uh option two and option three so we tie it with the max length of 15 words this will be the same next length of 15 words and lastly maximum of 15 watts so this uh straight output function here is going to return us with an array of five objects each object will have followed this a similar format okay so we're gonna tell the shape what's the shape of this right so we're gonna do uh type uh question it's gonna be you're gonna have question answer option one option two and option three okay right so we're gonna tell these questions is gonna be a question array okay so this is going to be a temporary type then after that we're gonna just return the questions that we have here so now that we've got this a get question from transcript we can actually use it here so let's call the get questions from transcript function from YouTube API and so when you're passing two things right when you pass in the actual transcript so we got we got that from up here right get transcript I'll try and strip and the second thing we pass in is the chapter dot title sorry chapter dot name so that's the title of the course okay so with that uh we have gotten the questions so after we have gotten the questions let us actually create the individual questions within the database so let's do uh await uh Prisma dot question dot create many go passing the data so we'll just do questions.map so we're taking the individual question and what I want to return we're going to return that object right so this object will be what's created in this data so we can see that everybody in control space for each question we have a few things we need we need answer chapter ID and options and question so the first thing here we can fill up the question is obviously the question dot question so this question type actually comes from uh this uh whatever we have up here this question type here right because we're returning a bunch of questions with this shape however we need to make this shape confined to others reinstall in Prisma so we also have the answer he's gonna have the question the answer right and then we need the options view so how to get options so what we can do is we can let options it goes to the normal array of the question to answer question dot option one question to option two questions do option three so with this uh array of four questions we can mix it up together so we can just do options equals to options.sort math dot random minus 0.5 so this will just Shuffle up the questions and lastly we can just pass in these options as option uh json.stringify options right because remember if you come back to schema uh pre-smart a schema a schema the Prisma I mean we can see the individual question will have options this will be a Json array right by the way we place it is in a string therefore you need to stringify this set in the front end where we need to get back to original options we can just do json.parse and we'll get back the array of the four Shuffle answers okay so then lastly we need a we need uh lastly what do we need we need this chapter ID so we can just pass it in the chapter ID as chapter ID okay that's good so after creating all the questions uh let's come down here we can now uh update the chapter right so we can do await Prisma dot chapter dot update right because remember right now we after the first step the first step only added the name and the YouTube search query but right now we still have the video and the ID and the summary therefore right now in this part we can now add the video ID and the summary part so let's do that so we updated this chapter we'll pass in the data so the video ID will be passed in as the video ID which we got in here and then the summary or passing as a summary so let's see why here uh data okay so we also need to tell you where the ID equals to chapter ID okay perfect so what we did here was we update the chapter ID with the individual with with this ID and we told it to update the video ID and the summary from the GPT output above and lastly we can just uh if everything goes wrong here we can just return a next response dot Json of success equals to true and we can pass in pass back uh we can just perfect the success equal to true so save it so with this uh root created we can now come back to our chapter card so now instead of just getting back a random data we can actually get when we hit this endpoint it will actually return us on whether the uh whether it has successfully gotten it right so uh come back to this chapter card so whenever we hit the trigger load we're gonna basically get this chapter info right uh uh a passing will pass in the chapter ID into this endpoint then this endpoint will hit all the YouTube apis and then it will return us with either success of true or success or false right we can see that if everything goes over it's written success of true if anything goes wrong you will return success or false then based on this success status we can then change the colors of the background to either a red or green so let's do that part right now so on success instead of just console the logging out success I'm gonna just do set success so remember the color of the background is determined by the success state right and so whatever we have here we can actually destructure the success part here and we can just do such success equals to uh uh true right we actually don't need this because we know that if it succeeds we know this x is definitely true then what else that if on error so if there's an error let me just get the error we'll just control dot error out the error and we'll set success to obviously false so we have the back rate background indicating that something runs wrong okay so after setting the success to false so it will be a red color background let's also get the tools so we'll just show a notification so let's just do on this tools we can get it from the use toast use toast right and we'll just do those off title of error error let's give you a description of let's say there was an error loading your chapter and then we'll give it a variance of sorry variance of disrupt destructive okay so that's it for now so let us see something uh let's see if everything is fine okay so the last thing here is right now we'll press generate right how do we know when everything has been finished generated so we can actually come back to our confirm chapters uh component here again and we can just keep another state for all the completed chapter right so we'll keep a ref state code completed chapters set completed uh chapters right it's gonna be react or used State uh right so it's gonna keep an empty It's Gonna Keep An Empty set so we're gonna keep all the ref keep it in a set so this will have a set of string okay let's save it so basically what we're gonna do here is that uh whenever one of these uh request comes back as either success or false we're going to add the ID of the chapter into this completed chapters so whenever all of this has finished processing then we can basically set the generate button to another state so that we can press like okay save and continue right so let's have another state here for the total chapters count right so we want to see because we want to see how many total chapters are there so total cons total chapters count equals to react dot use memo okay so let's just return course dot units dot reduce right you're gonna take accumulator in the unit right we just want to add the return the accumulator plus you need dot sorry units dot chapters dot length so we can initialize it to please just be zero then pass in empty dependency array passing the cost dot units save it so what this does is it goes through each unit right and then because each unit has let's say this has three chapters three chapters three chapters so for each unit you will just add up the chapters to the accumulate accumulator and then at the end we return the final unit so in this case it will return us with 12 total chapters so this variable will contain 12. okay so let's also have a loading state so up here cons loading and set loading it goes to false so whenever we click on this uh generate button right let us set the loading to be true okay so then we want to disable the generate button right so let's disable it disable uh disable it when the when it's loading okay so I'm gonna have another thing here so whenever I finish whenever we finish right we no longer want to show this generate button we actually want to show another a button for it to go to the next page right so what I'm gonna do here is uh should I okay come here I'm gonna come up here I'm Gonna Save the file here so I'm gonna try it out first I'm gonna try and make sure everything is working so when I press generate what will happen is uh it's gonna basically call the trigger load function on each of the chapter card right and then it will just call this get chapter info and then it will set the success to either true or false so let's click on this uh click on this generate uh let's see if anything's working so uh let me just here I'm going to refresh the page first okay so right now we can see that there's a lot of Errors right here uh let's look at this why Json format is failing so I'm just gonna re refresh the server and let's try it again so I'm gonna just refresh it and let's see so I'm gonna do here is I'm gonna press generate right it's gonna then hit end points individually and hopefully if everything goes well it will start turning green or either red depending on whether we are able to get the summaries and the transcript okay so right now you can see there's a lot of Errors right where else one of these got back correct uh this is also correct so you can see right now there's a lot of invalid Json objects uh questions okay okay that's good so we just have to wait for a while so it doesn't really matter if it's correct or not so right now we can see that all of them has finished processing it's just that some has errored out so we fix the error out later but all we want to do now is that it doesn't matter whether it's Error or not we just want to make sure that everything is complete so when everything is complete we want a button to go to the next page right so I'm gonna come now to these confirmed chapters uh here this common characters comes out the chapter card okay so if I have already processed it right I don't want to reprocess it again so what I'm going to do here is um on this trigger load I'm gonna just check if Pro if the chapter dot video ID exists so basically what I'm saying is if I have already generated the video ID so I've already done the question generation everything right there's no point of redoing it and we're seeing resources again so what I can do is I also want to set the I want to be able to uh add the com adding to this completed completed chapters state so how do I do that so remember whenever we finish processing we want to add ID to these completed chapters so what I'm going to do is I'm going to pass this state into my uh into this chapter cut so I'm gonna pass completed chapters equals to completed chapters set completed chapters equal set completed chapters okay so we're coming to chapter card we'll ex will receive these things as props so completed chapters is gonna be a set of string and the set completed chapters is going to be a react dispatch of the set of string we'll come down here a set of string okay of course because the casing matters so right now here we can see we're using lowercase so let's just set it to upper case string and this also has to be uppercase string to match whatever we have up here so right now we can see that of course we're using uppercase s here so let's save this and this and what we can do here is I'm gonna check so if chapter.video ID right I'm gonna just do uh right now function called add chapter I cons at chapter ID to set equals to react callback so this callback will just memorize the function so that we don't recreate this function every time the page of the component we renders right so we're gonna pass in the chapter so what you want to do is we just want to get the set chapter completed chapter and the completed chapters from it we just want to create a new new set to new set new sets equals to new set of the completed chapters and then we're gonna do dot add chapter ID and finally set the completed chapters to new set so all you do is pass in the completed chapters chapter ID and set completed chapters so all the function does is that you will just add the current chapter ID to the completed chapters up in the parent up here Dr composite chapters and then so if the chapter the video ID is already processed I'm going to do an early return so that we don't unnecessarily hit the endpoint and up here I can just do at chapter ID to set okay and let's see here on success here we can also call this function at chapter ID to set because we know that it has finished processing and we can do the same here for add chapter ID to set so save it and let us just refresh okay so right now I'm gonna do something else uh I'm gonna do one more thing up here so above this uh imperative handle I'm gonna do react.use effect so I'm gonna check if the video ideally already exists chapter so if chapter dot video ID right I'm just gonna do uh what do I do I'm gonna set success to True right because we know that he has already been processed before it has already been processed right then I also want to just add the chapter ID to set so lastly you just add this as the dependency so I'm gonna save it and right now we can see that uh these few things has already been generated so it's lastly this so if you press generate you'll now try to generate uh this part here so let me add one more animation so that when we are generating it right I want it to show a loading spinner here okay so in this case all of it has been generated so let me just come down here to one more thing here so below this H5 I'm gonna add a is loading so while it's loading so whilst hitting the end point I'm gonna show a loader 2 icon from Lucid react with costume of animate spin receive it okay so once everything has finished processing we want to change this button right so I'm gonna come now to confirm chapters and I'm going to do this total chapters count so underneath here right so uh is loading so I'm gonna just come down here so here so I'm gonna check uh if the total chapters count equals to the completed chapters under size right so because these are set right so we have this dot size attribute right so if uh if it's already completed processing everything I want to show a new uh button right uh say go to next step otherwise if he has not completed I just want to show the normal generate button here so I'm gonna move this up here okay so uh there's another mistake here so when we click on this generate by write this add chapters ID to set right uh instead of uh basically creating this new set only you do is we'll come out here delete all these three things so we need to just set the completed chapters right we're taking a function so we're taking the previous uh completely completed chapters right we're gonna first do new set equals to a new set taking the previous completed chapters then do new set dot add chapter chapter dot ID and lastly we can just return the new set okay and then the dependency array will obviously change also so this will just take in the chapter the ID and also set completed chapters so if we save this and we refresh the page so what's going to happen is when press is generate it's gonna check that oh I did have a video ID so I'm gonna do add chapter ID to to set it's going to call this function it's gonna set the completed chapters by adding it to the ID and then eventually once we have reached once all the chapters has been finished processing this will become true and then we'll show this go to Next Step so press generate we can see that immediately this go to Next Step has triggered because all of this thing has been finished processing so for this uh link component list link component uh let's first give you a class name of the button variance okay this button variance is gonna have the class name of margin left of 4 and font semi both let's save it so this looks better now and then lastly is uh we can just do save and continue give you a nice share share run right icon and then the costume will be with a four height of 4 and margin left of two so we'll save it and now it works so this href will actually want to bring it to so let me show you what my plan is for this so if you come down to the normal the original website here we can see that for each of these uh for each of the courses how we basically display the course is let's say we click into here right so the first thing here you if you look about the URL is that we have we map it to slash course we pass it the course ID right sorry the course ID it's right here we can see that it goes to slash course the course ID and then we have other two things here so this will be the unit index and the chapter index so if you come down to let's say uh this part here we can see that this has changed to unit 0 and in a chapter one so if you go up to one more up here we can see that it has changed to uh unit one unit 0 and index two then if you go up to like let's say here we can see that the URL goes to a unit 2 and index one so basically we are using this uh indexes to basically uh index into the units and the chapters within the courses okay so the last step here is basically uh the href here will just lead us to uh Slash course slash course the ID slash zero slash zero so remember this slash yo slash row just means that we're going to the first unit and the first chapter within this course so let's just save that and then everything should be working fine so I'm gonna refresh the page and when we click generate we can go save and continue and then the next step will be create the course page so I'm gonna just tie up everything I'm going to just create normal course to make sure that everything we have done so far works so I'm gonna create something like let's say World War Two uh it's about atomic bomb atomic bomb and open Heimer I'll remove this unit so press that's cool so remember what it does it will take these units it will generate the chapters and the YouTube search queries and you'll save it to the database and then if it works you'll bring us to the next page where we then can confirm all the chapters and then uh generate all the YouTube apis so we can see the SAS cost has been created successfully so all the units and chapters has been created for us so all you do now is Click generate so that we can see that now it has this is a loading spinner beside beside it and hopefully as the apis get fulfilled one this will be solely uh turned to green or red depending on whether it succeeds so you can see that it's working fine it's working good so all the questions are generated or the summary is working and sometimes you know like uh the chapter there's an error loading the chapters but this could be due to like maybe the YouTube API being rate limited or just some something else so we can see that when all of this is completed we can just press save and continue or we can refresh the page here so we can see that if the video ID already exists it will just turn green and then we can regenerate the others so we can see we can just try to try our luck right because when we talk about YouTube apis and like AI apis we are not always sure that it will return us with the correct so we can see that sometimes some will return a screen Sometimes some will return as red so we don't have much control over that it's not very deterministic in that sense but if you can find a way to make it more deterministic do tell me about it then we can improve on the app together anyways uh we have done good for this part and so the next part will be actually creating the next uh the course page here okay so to create this page where we have the course ID and the unit and the chapter index so we'll come down to our directory and so what we need to do here is uh all you need to do here is just come down to our here so under the under our app folder here we're gonna create a new uh folder course inside here will create a new folder called uh dot dot dot slug and inside here we'll just do a page of TSX so what's the difference between this dot dot dot and the normal angle normal bracket like this without the dot dot dot so if you don't have a DOT before it this course ID it will just map to slash create slash course ID well if you have this dot dot basically any path that comes off of course slash whatever is at the end you'll all be handled by this page or T6 so let me just show you the example so from the little TSI fce I'm going to just do a course page I'll just save the file so right now we can see that cross page is being rendered here and then here how do we actually get the course index so right now here we got three parts right we got three parts so the first part is the course ID which is the course ID here and then the second part will be the unit index and the last part will be the chapter index so how do we actually get it so under the props here next yes I should pass us the params inside the params we have this slot which is a array of strings so this slot will actually correspond to whatever you name it here within the square brackets okay so we can actually destructurate so params and within it we can get the slot so let me just convert this into a async component and let me just show you what the slug is like here so I'm gonna just put the json.stringify slack now two let's save it so here we can see it's an array so the first item is going to be the course ID the second item is going to be the unit index and last ID is going to be the chapter index okay so with that we can actually destructure it so first let's get the chorus ID uh you need index param and the chapter index program we can actually destructure it from slot okay then let's get the actual course so we do cons uh cons course equals to await Prisma dot course Prisma let's import Prisma dot course dot uh find unique uh where the ID equals the course ID okay and then let us also do the join so we have to include the units and then include also the sorry include the chapters okay so let's just uh check so uh if the cost doesn't exist right that means uh it's a 404 so we'll just return redirect let's redirect them back to slash Gallery slash gallery okay and then we want to get the unit right so we do let's unit index equals to parse int you need index param we'll do the same for the chapter index so then we can actually get it so remember that our course contains all the units and chapters so we can actually just get the units and chapters by doing a const unit equals to course dot units at the unit index so if either of the unit right then I can just return redirect to just the slash gallery okay then um let me just show you what the course looks like so uh let's show you the course okay so we come down here to the course we can see that this course will have the units so the units is an array right therefore we'll get index into the unit index program so if the unit index is zero we're gonna index into the first unit here and then within the unit we want to get access to the chapter right so we can just do cons chapter equals to unit dot chapters add the chapter index then lastly we can check if not chapter return to Gallery okay that's good so now let's actually do the UI for this so we're gonna return the first thing we're going to return is the course site bar so we'll get create a new component called the cross sidebar so this core sidebar is basically um so let's come back to our Learning Journey example here so let's go to the just a normal course here so the sidebar will be what is shown on the side here so we consider the the course name and also the bunch of units where we get to like navigate around okay so we have to navigate around so how do we let's create that component so come down to our components folder let's create a course site bar dot TSX tsrafce and save it and now we can import this class sidebar okay so this course sidebar we will require a few things right uh the call Sidebar will require um let me see sorry course sidebar let me just refer okay so uh the course sidebar will need the course itself the course is going to be of type course from Prisma Clan we're gonna join it with the units which is a array of a unit uh gonna be a real unit so this from Prisma client join with a question with the chapter which is going to be a chapter from Prisma client then the whole thing you need here is a array so this is the proper type because we're doing the joins here so then we can pass in the course don't be the actual course okay that's good and then uh we can also come down to cross a bar okay so now let's build the UI for this course sidebar so first I'm gonna just destructure the course here uh this can this is gonna be uh this can be a server component so let's return with a div with the class name of width of 400 pixels uh position absolutely top of half negative translates y of half padding of six rounded rights of 3XL and background second three okay within that big diff we'll have a H2 with a class name of text small text SM let me see do we have text SM no sorry this hits uh it's not a hitch 2 is actually a H1 so this will be the main course name here right so let's just name it the text for Excel of sorry text 4 XL fonts both and inside of it we can just put the course top name so let's save it and right now we can see that we have this uh the course name showing up here so we got this cross Side by going then uh just underneath the H1 uh I'm gonna map through the unit trade so for each unit we can show each of these individual sections with the separator so I'm gonna come down here so below the H1 I'm gonna do course dot units dot map for each of the units and the unit index uh I'm gonna return a diff uh a div the key will be the unit dot ID okay and then within the div uh let us give you a classmate of margin top of four inside the div we have a H2 that will display the units uh with the units units index plus one and then give you a class name let's give the H2 a class name of text SM upper case text secondary Dash foreground opacity of 60 so right now we can see this unit one you two three four five okay below the H2 will display the actual unit's name for this the classroom will be text to excel fonts of both save it so here we can see we got this we got the uh we got the unit names so this will be the text to excel font pop okay so uh underneath that will actually map through the individual uh chapters so go back to the individual chapters so under the H2 let us now do unit top chapters dot map the chapter and the chapter index uh okay so we're going to return a div this div is going to be an empty div with a key off chapter chapter ID within the chapter we'll have this a link so next slash link uh so here next link here so what do we actually want to lead to href so it's gonna lead to just slash uh course slash uh Slash core sorry slash core sorry course.id slash uh unit Index right right and then lastly slash chapter Index right so this is how the URL structured and then within the the link let us just uh show the chapter that name we also give you a class name so class name equals to CN import the CNN function uh for now we're just going to put the text second say can theory for ground by 60. let's save it so right now we got all these links here showing nicely yeah okay so sometimes it's too long all right so you can scroll down and stuff so in this case it's a bit too long right so it's okay let's continue coding out first then we can uh minimize it a little okay so after this after the chapters your name under the link after everything right uh after everything we want to have a separator right so we have this a gray line to separate the different units so we come up here just before this uh closing unit we'll just have a just before above the closing diff let's add a separator uh per reader from UI let's give you a classroom of margin top of two text Gray of 500 BG gray or 500. let's save it and now we have a nice separator for each of these for each of these links for each of the units okay so one more thing you want to add is let's say when an original right whenever you're on the chapter is highlighted green so how do we know whether uh it is uh we're on the current uh Wikipedia or like Which chapter we are on so what we can do is we can come back to our page right we can pass in one additional prop code Uh current chapter ID right we can just pass it in to be the chapter the ID right this chapter comes from our chapter index here so we accept this so we'll come up here and then accept one more chapter Uh current chapter ID it's gonna be a string where we can extract it from the prop current chapter ID so in our CN here right here right we can pass in the second argument and object we will see that uh we want the the text will be text green 500 and fonts of fun both right if the current chapter ID equals to the current chapter ID right so if you save it it'll come back here we can now see that uh if you come to the first link here it should be hided green let's refresh the page uh so right now we are on zero uh so let's save the file up here also and then now we can see it's working so if you go to another link we can now see that the link the green color thing is changing to whichever chapter we are on right now okay so that's good we have completed a chapter card so we have now also completed the core sidebar so the next step here is to create uh this middle part this uh where the video and the summary lives okay so coming back to our slab so you're stuck here so other than the cost uh sidebar look at wrap this whole thing into a difference so I'm gonna wrap it into a div put the Cosi bar in it okay then just return this okay so underneath the cross sidebar will have another div okay so this div is gonna contain the quiz and also uh the video so let's come back to another example where we will see the the questions and also the examples the video so maybe in this part we get to see the questions uh let's see if we can find one with the questions okay so this is a good example so right now we have this call Sidebar right then we also have this part and also the concept track here and underneath this tool this will have a previous and next buttons so let's continue coding it out so within this diff opening diff uh we have another div in here so this div will have a class name of margin left of 400 pixels and then a padding X of 8. then within the div let's have a div give you a class name of flags okay so 20 CSS then in here we have the component the main video summary so we create a component soon so uh and then underneath here we have a quiz cards okay so this is how we got layout So within this Flex box here we can put both the the video summary and the quiz cards here so let's create the main video summary first so come under the file uh we'll create a new component so golden component and create main video summary dot TSX tsrafce we'll save it okay so in the main video summary uh let us we need to accept in the chapter right so for the props we're gonna take in the chapter so it's gonna have a type of chapter okay so in here we can actually import this up here first and then we can pass in the chapter to be the current chapter Okay so one more thing here we can do is let us just uh we also need a unit right because you've come into the original example we can see that we got this unit 3 and chapter two so let's try to get the unit index and the unit basically so here I'm gonna also accept we also need the unit to be a unit we need a unit index number and lastly we need a chapter index which is number so I'm going to pass it into this component the chapter index equals to chapter index the unit is the unit and the unit index is the unit index okay that's good so for the main video summary so let me just comment out this quiz cards first so it doesn't show the error okay so right now we can see the main video summary is being shown here so let's code out the UI so it's gonna be a simple thing here the div the diff is going to have a customer Flex Dash 2 and margin top of 16. within the div we have a H4 right they will say unit you need uh unit index sorry let us destructure the props so you need you need index check chapter and chapter index so you need index plus one right and then we have a a bullet bull and then we have a chapter index plus one so if we save it we can now see that we got this unit one that's one you need one uh here we can show s chapter one okay so let me just show up here so you need one that's chapter one so give the H4 class name of text SM upper case text second Ary foreground of 60 opacity so it just looks a little nicer right here okay so uh underneath this part here I'm gonna underneath the H4 we're gonna come down here and we'll show H1 of the a chapter the name we'll give you a class name of text for Excel and font mode we'll save it okay so now we've got the chapter name below the H1 I'm gonna put it frame video so remember we got the video ID right so we're gonna just put the iframe here so the title is gonna be the chapter video okay and the class name will put width of full margin for aspect of video uh Max height of 24 Ram so I was just playing around the numbers this is what I found to work then for the source right for the source it's gonna be a https color such as www.youtube.com embed slash chapter Dot video ID so remember we got the video ID from the YouTube API so we can just link it up into this YouTube embed and then we'll do allow full screen so we save it we can now see that we got the video here playing based on the video ID that we've gotten perfect So Below the iframe right we'll just have a div with a classroom of margin 24. so this for the summary so have a H3 that says a summary and just give you a class name of text 3XL font samimbo then here we can have a p-tech and we can just put the chapter not DOT summary we'll give you a class name of margin top of two text secondary dash for ground 80 opacity and we get to see the summary here okay so that's good that's really good so this summary comes from the transcript okay so we have completed the main video summary so now let's actually work on the quiz cards okay okay so we're doing great so let's continue working on the quiz cards right now so if you go back to the original example right so the quiz cards is gonna basically uh will have about multiple questions each question will have the options and basically when we select all and we press check answer uh it will light up a red or green uh signaling whether we got it correct or wrong so how to actually get access to the questions so right here if you come back to the page remember that under the chapters each chapter right we can actually do another join to include the questions itself the questions uh sorry questions to be true right so now we have access to these questions so under these quiz cards uh underneath this quiz cards what I'm gonna do is uh we'll create this component first so let's come down to the components folder and let's create the quiz cards dot TSX tsrafc okay so we'll save that okay so the quiz cards we're gonna take in uh for the props we're taking the chapter and the questions uh itself so take it let's take in the chapter so it's gonna be chapter uh and then it's gonna be uh joined with the questions so this can be just a question from Prisma Clan it's gonna be a ray of questions so underneath a piece of TSX let's import this quiz cards okay and we'll pass in the chapter into the prop okay so now that we have these uh quiz cards let us actually make the haste the UI for it so let me just destructure the quiz the chapter from the props first so let us just return return a div so this div will have class name of flex of one okay margin top of 16 and margin left of eight within the div we'll have a H1 right that says concept check okay so this history has a class name of text 2XL fonts of gold business H1 let's have a diff this div will have a class name of margin top of two inside this diff is where we actually map all the questions and answers so we do chapter DOT questions dot map right so for each question what do you want to return the question so I'm gonna return let me just return this okay so let's just build the UI so return the div first so this div will have the key of question dot ID okay within this div here is where we actually put the uh the question itself so let's just um give a H1 sorry H1 that just says question dot question so this is the actual question title itself let's give you a class name of text Dash large and fonts semi bold so for the wrapping div right let us give it a class name right we'll use the CN utility so for this CN we're going to have the Base Class of padding-3 margin income of four model boulder dash secondary and rounded large okay and then basically we'll have we're gonna come down to our H1 right so let's save it first and let's see how it looks okay so right now I've got this concept track and in this case we don't have the uh we got all these questions so let me just under the H1 I'm gonna have another diff diff right this diff will have a calcium of margin top of two all right so here I'm gonna just type in um so we have the we need a radio group component right so if you come down to chat CN UI so they have a radio group component so this is what we'll be using to help us with our UI so I'm gonna basically download this component into our file so open the command line and let's do just paste it in set CN at radiogroup so press yes to install it into our repository so let me close the terminal So within this div right here's where I'll put all the question so we have the radial group from that slash UI okay uh within this radio group we're gonna put the default value as just comfortable wait sorry shouldn't be here uh actually within the radio group we can actually put the radios so how do we actually get the options right so remember our options is a Json stringified array so before that we can actually extract the options equals to json.parse question dot options right so this option is gonna be a array of strings okay so then within the radio group I can then Loop through the options options.map for each option and the index what I can return is uh let me just return div with a class name of flex items items Dash Center space decks X of 2 will give you a key of index within the DFS put the radio group item inside here import it from UI so the value is gonna be the option itself and then in here we'll pass in uh the ID so ID is gonna be the question dot ID plus index.to string okay and lastly inside here we'll just put a label from the slash UI label and we'll just put in the actual option to repeat this plate then for the html4 attribute we have to do question.id plus index.to string so why would this html4 level is in here has to match exactly what's in here for the label to work properly so let's just go to a question let's go to a topic where there is okay so in this case this Adolf Hitler part so we can now see that with this concept here okay so right here we don't see the options let's see if you're able to find a why that is the case so we're returning okay so I have a mistake here so this radio group item should actually just uh be a self-closing tag so remove the last video group item and if we save it then now we can able to see the labels uh for the concept checks right then when we click on it we can see the radio buttons working okay that's good okay so after starting to use client how do you maintain a state right so I want to hold the set of which uh answers did they we did the users pick for each question so I can have a state here right this will just be cons uh answers and set answers okay this will be a react.you state this is gonna hold an object so what does this object hold this objects can hold a record between strings and other strings right so what this answer is let me just demonstrate so it's going to hold the question ID so let's say question one to ID it's gonna hold the answer to it so in this case I say they choose option one this uh Socialist Party right then usual Socialist Party so we're basically storing the ID to the answers that they have they have chosen here so whenever we change and click on the radio buttons we want to change these answers so how do we do that first so for this for this on value change prop this after using this e what we can do is we just want to uh set the answers we're taking in the the previous answers right and we just want to uh return a new object right containing all the previous answers except that now we index into question ID and set it to the new value so let me just console the log the answers so that we're able to see what is being shown within the object itself so we can open the console here so right now if you clear it we can see that every time we change it so this question ID is mapped to the answer right here right so if you change it to like 7 here uh press seven we can see now that there's a new uh seven being a mapped to the question ID so that's how we hold the answers okay so that's good we have the answers so underneath here let me add the button so that they can actually check the answers themselves so underneath uh underneath the div here right before the closing diff I'm going to add a button okay this button will have we'll see check answer okay so the button we have a classroom of with just full on imagine top of two will give you a large size of large and basically on click we want to check the answer check answers okay so under here let me add another icon Chevron uh right we'll give you a class name of width for height four and a margin left of one so let's create this check answer function so coming up here I'm gonna do cons check answer equals to A uh react dot use callback so we're going to memorize the function so this function what does it entail so first we're gonna basically have another state to hold like the weather which question is correct and which question is wrong so we'll have a question State and set question State it's gonna have the same type as the answers except that this time but this time instead of having a record of the question ID to a string right record instead of the question I need the string will be the question ID to either a Boolean or null so let me explain what this means so for this question State we'll be holding the question Index right so it could be like question one dot ID and then this could be the true so if it's true that means is correct then we'll light the background screen then if question id2 equals to false will actually read but this could also be question three dot ID equals to now if it's now that means that user has not answered it before therefore we'll leave it as a default like a background so we'll be keeping these kind of things inside this question State and then we'll use this question state to actually trigger the background color change okay so whenever they click on check answer we want to update these questions state right so what do we do we just first do new question status right let's just copy over the old question State first okay uh new question okay so after the new question State let us then do uh const answer uh let us do sorry maybe chapter each question itself so for each question we're gonna check we can get the answer for the question so answer will be answers and question dot ID right so because these remember these answers uh actually uh these answers come from the answers right here so let me just pass an empty dependency over here first so these answers will be basically what the users has answered right so we get the user answer so let me just rename it to user answer okay so if the user has not answered it right what do you want to do what's your return right because we're going to leave it now and leave it blank How about if the user answer equals to the question the answer so if they answer it correctly we can index into new question status this new question state in the question the ID and we'll set it to true else we'll set it to false right here okay then in the end we'll just set question status uh set questions state to the new question state so the dependency array requires a Fielding answers uh questions uh question state and also chapter top questions okay so with that we have this function then we'll be calling it uh down here check answer sorry check answer so with that we can actually display it whether it's green or red depending on whether it's correct or wrong so underneath here uh underneath here for the class name here as a second argument I'm gonna add another object here I'm gonna add we want to show the background of green 700 if the question State at the question.id equals to true so if that's correct we sold as as true if it is correct which one has green if it's wrong right if it's false we show as red otherwise we just want to show it as background secondary right if it goes to now so let's save it and then let's see how it works so at first everything's gonna be now but as I answer this I answer the first question here and the second question I press check answer we can see that only the two write-ups are red so I'm gonna try uh keep trying so in this case if you get it correct you'll light up a screen and the others are not lit up because we have not answered them yet so if I answer them uh here we can see that the answer the feedback will be given yeah so that's how the question answered and works so yeah okay so we're doing amazing so we're almost done with the project so we can now see that the this question page works right we're able to see the course and and stuff like that so uh let me add one more thing below here so we in the normal Learning Journey we can see that we have this next and previous right so what we're gonna do is I'm gonna come down here I'm gonna come down to the slot page so this is where we put the main video summary and the quiz card right so underneath here I'm gonna actually get at the previous and next buttons okay so underneath uh the quiz cards the div so just before the three closing diff down here I'm gonna add a a separator so it's gonna be a self closing diff instead of a customer Flex of one and we'll have a height of one pixel and then margin top of four text Gray or 500 and also background Gray of 500 so this will give you that uh separator below here you can already see it right now but less coding let's keep coding and then we're here we have a div so this div will have a classroom of Plex and padding bottle of eight here so now we can see that we have this separator here this separator okay then within the uh within here we actually do the previous chapters and the next chapter so how to get the previous chapter in next chapter so just above the return let's actually get con's next chapter equals to unit dot chapters chapter index plus one so we index into the next chapter then a brief chapter is gonna be the same unit dot chapters except that we index into chapter index minus one okay so inside the padding bottom eight div we can adjust check Okay so if there's a previous chapter right what we're gonna show we're gonna show a link from next slash link right within the link will give you a class name of flex margin 12 4. margin rate of Auto and width of fit okay and then we'll inside the link will have a div that has a class name of flex items Dash Center within the div we have a Chevron right icon sorry should we share a run left icon left with a classroom of width of 6 height of 6 and margin right of 1. then below the Chevrolet left we have another div this time it has a classroom of flex Flex Dash call and items Dash start then inside the div right will have a span that will say previous and then under this the span we have not spent double says previous chapter dot name so this first pen we're gonna give you a class name of text Dash SM text secondary Dash foreground divided by 60. the second span will give you a text XL and fonts of bold so I'm gonna save it right so right now we don't have any previous chapters right because in the first chapter so if you go to a chapter like this okay so the HF requires a href right uh this link requires hit ref so think about where do you want to put the href link to so it's gonna basically link to ID right slash the current unit index except that we just want to uh we want to we need to go back to the chapter index -1 so let's save it and now we get this part so we can see that there's a previous so if you click on this previous we go back to the previous chapter on Adolf Hitler okay so now let's do the same for the next chapter so the next chapter will be very similar so I can just copy this previous chapter code here I come down here I'll paste it down here so it's gonna check if there's a next uh chapter right the href will actually go to chapter index plus one right in this case instead of margin right Auto you go to margin left Auto okay then everything else is going to be very similar except here we'll say next and then here we'll see next uh next chapter name okay so let me see what else we have right here uh okay and then instead of the seven left being up here the Chevron left will actually come below the div so I'm gonna copy this uh sorry I'm gonna delete this and come down below this div and put shave run right icon the same class name with six height six and margin left of one we'll save it and right now we can see this next so it goes to the next Winston Churchill and so that's pretty much it so we can see you can go to previous and we can go to next yeah okay we are doing really good we're almost done so now since we are finished with this uh these Pages let us just go back to making the gallery page so right now we can see that the gallery page uh does not exist so let us create that right now so come down to your file uh come down to app let's create a Gallery file Gallery file inside this Gallery folders in to a page or TSX and do tsrafce so this is gonna be Gallery page let's save the file okay so now we have the gallery page being shown right here so let's give it a while so let's refresh the page and now you can see the gallery page uh being shown here okay so for the gallery page it's a pretty simple it's a pretty simple page right we're just basically looping through all the courses and just displaying it okay so what we're gonna do here is uh I'm gonna come down to the Gallery page and let's just get all the courses first so Michael is async component so it's gonna be a server component we're gonna get all the courses so cons courses equals to await Prisma Prisma dot course dot find many right you want to include so we are joining all the units and within the units let us uh join all the chapters include chapters equals to true okay so with all this we want to what do you want to return let's return a div with a class name of padding y of eight MX Auto uh Max width of 7xl and then if you come down here let's do the display grid so we'll display in a grip grid so we have a display grid here so grid grid calls of one so one is on the small screen get by four so when it's a small above small screen let us do grid calls that's two whereas on medium screen read calls that's three it was a large screen let's just do grid calls that's four then we'll do Place Dash items Dash Center okay then inside the grid we can just map through each courses so courses.map so for each course we want to return a gallery course card I want to pass in the course into it and of course when I'm passing the key to be course.id so let's create this Gallery cost cut component come down to your components for file and let's create gallery course card dot TSX t s r a fce right let's close the sidebar so it's going to accept the course uh the course right it's gonna be a course it's gonna be joined with a bunch of units so the unit is going to be a unit here join with the chapters is going to be chapter from personal client and array and this will be array also we'll save it and let's import the gallery course card and so we can pass in the course and the key the cost of the ID so right now we can see the gallery course card here so let us so right now we have three courses right but how do we actually display it properly so let's go into the Gary cost cut and it's an async component right I'm gonna just destructure the cores from here so the UI is going to be uh that's wrapping a react fragment we're gonna do have a big diff out here let's give you a custom off border rounded uh rounded of large border secondary within the div I'm gonna have another diff that is just has a class name of relative okay right so let me close this diff okay So within this relative div I'm gonna have a link so this from next slash link okay this next slash link is gonna have a href this href is gonna lead to slash cores slash course dot ID the first unit and the first chapter so this link is gonna have a class name of relative display of block and width of fit within this link let's have a Nexus image okay so this next image will have a source this Source will come from the course.image if not we just have empty string okay the class name here will have object cover with a full Max height of 200 pixels okay rounded top of large okay so I need a class name will give you a width of uh 300 fixed width of 300. and the same for the height 300 pixels and then for the out let's just put pick picture of the course okay so let's leave it and right now I think that will be an issue because we need to White leave the domain from pixels uh not pixels but from unsplash so we'll wait for you to allow for refresh and right so we need to add this hostname to the next config so I'm going to copy this close name come down to my next config.js so other than the lh3 Google user content we also need this S3 us2 right so we make a change to next config.js so we have to restart the next JS server so we start it we close it and now if we refresh this again it will show up properly okay so that is good so whilst refreshing if you come down to the image we can actually have another span so this pen will actually contain the courses name course.name okay my computer is like slowly lagging out but it's okay so this band will have a class name class name of absolute padding X of 2 padding y of one text Dash White okay so it's really slow right now uh rounded medium background black uh background black off 600 okay so right now it's working it's just that the code right now is really slow I think because my OBS is taking up so much RAM but it's okay so we have a background black right divided by 60. a width of fit bottom of two left of two and right of two so let me save that and we'll see how it looks like in just a moment so you can see the cost titles up here right now okay so that's good so then underneath uh all the div right just before the closing div will now have another div here we have another diff with the class name of padding of four okay inside this div we have a H4 that says units right the casting will just be last name will be text SM text Dash secondary Dash foreground divided by 60. okay let me format that then below the H4 we have a div with the class name of a space that's why that's one inside it will map through the course in units so we do course dot units dot map so for each unit enter unit index I'm gonna display uh yeah display we're gonna return a link right this link would will say units unit index index plus one then we'll pass in unit dot name okay so this class this link will have a href of course slash course.id slash you need you need index sorry you need index in the first chapter okay so let's save it so you also need a key prop here so it's okay you're passing the key will be you need unit Dot ID okay so then we also have a class name for this link the classroom will be blocked underline and with off fit okay so let's save this and let's see how it looks so you just display all the units underneath the cards itself okay and that looks uh looks pretty good to me so we can now see that the units are being shown here so if you go back to the original this is what it looks like and it matches so right now there's some duplicates in terms of this unit so what I can do here is I can just remove this unit here so we just display the unit name and now it looks a little cleaner okay that's awesome okay so we're actually really close to being done so the last step here is just to integrate the stripe and then after we integrate the stripe we can then uh proceed to deploying it so I'm gonna just come down here to the create course page right so underneath here we can see that under the form we want another card here right so this underneath the card here is going to show how many credits they have left how many three generations they have right so let's come back to our let me see create a create course form uh what's above the create cost from says create so paste or TSX under the create So Below the create cost form right so what I'm going to do here is we're gonna display a nice card for them to show to see okay so um actually we can go into the create course form and can put it underneath everything here okay so let's come down to the quick course form and here I'm gonna have a component so go all the way down all the way down so after the form just please for the closing div I'm gonna have a card that will do subscription action okay so I'm gonna create this component so cut down to your component folders and create a subscription action.tsx tsrafce okay we'll close the sidebar and then we can now import this subscription action okay and then we'll save it okay so we got this subscription action here right so let me just do the the UI for you so we're gonna return a div with a class name of flex Flex Dash call items Center width of half pending of four MX will do margin 204 rounded rounded medium and also background secondary okay let's save it so now we got this card so within the card div right I want to be able to get the user right so how do I get the user so because this is uh this is going to be a client component right use client right we need to get the user session data so we can just use a there's a Nexus actually provide us with a new session hook for clan components So within this session we can actually get out the data so this data will contain the user itself okay so we need this div Let's uh just want to show data dot user right subscript.credits right so 10 free Generations let's save it so right now we can see that uh this session must be right in the session provider okay so why what is this showing here is basically if you want to use this new session you must wrap our app in the session provider so let's come back to our providers.tsx file and let's actually wrap it in the session provider okay so let's import the session provider from next off slash react then within these children we can just wrap it in the session provider then wrap the children inside it and then we can see now that after wrapping it we're able to see the 10 out of 10 future Generations so this is a very Dynamic it's Dynamic to the data user credits okay so after we have that let us then build the rest of the form the card so we have me another component for the progress bar right let's just do okay we'll press yes to that so that it will install it into our Repository so underneath the three generations I'm gonna import the progress from the slash UI okay the value of the progress is gonna just be the data Dot user.credits right if it exists right if it exists we'll show this if not we'll just show it at zero so if there's no credits we'll just show as zero but if they have credits we gotta show data.user.credits right uh divide by 10 divided by 10 then multiply it by 100 and this will basically show a progress Mark so if it's 8 out of 10 generation it will just show up a out of 10 in terms of progress we'll just give you a classmate of a margin top of two okay then underneath the the progress I'm just gonna add a simple button right the button will just say upgrade upgrade we'll give it a icon called zap sorry this should be just upgrade upgrade up great right then we have a zap icon right let's give you a costume of field Dash white and margin top of two let us save that first so for this button we have a few class names for it so it's gonna have a classroom of margin top of three front Bolt text white transition background Dash gradient to top right right so what's the gradient so we're going to grow from a from Green 500 to blue 500 then when it's hover we do from Green 500 then hover we want to do two blue 600. so let's save it so just give the button a nice uh gradient so that it looks a little cooler right so let's wait for it to actually load in transition PG gradient.2 TR from Green so here should be from Green okay let's hope everything else is correct and then the button okay so the button looks nice now okay uh so this uh zap icon should be much enough of two not margin top of two we'll save it and then it looks good now so let it set up right now so how do I set up strike so first come just search for stripe so just click on the first link that leads you to the stripe home page so we're gonna go to dashboard so you have to log in or create account first so after creating an account come up to your uh whatever you have here and create a new account and so I'm gonna just name the account learning Dash Journey Dash YouTube uh just put your country of operation then press create account so you just have to enter your password so that you are able to access certain parts of the dashboards okay so after creating the the new business account right where you need the basically we need the API key so once we're on this dashboard click on this developer developers Tab and here we are able to get our API key so we need this secret key first so let's get the secret key right let us go to our DOT EnV and we have to put it in here so we do stripe API key we'll put it as the API key here so this copy from the uh this copy from the dashboard here okay so then let's go back to our project here first and let's create a new folder so under slash API let's create a folder called stripe and then inside the stripe let's create a root.ts okay so the another thing we need is go under your slash uh we need to install the uh stripe library right so let me just install it by typing in npm install stripe so go into your lib folder let's create the strike block yes so we prefer the strike stripe Library so the first thing is let's import strike from stripe that we just downloaded they will export cons stripe equals to new stripe right we need to pass in what do you need to pass in when you pass in the process.env dot stripe API key right though you just copy from the stripe dashboard right we make sure that is exists and then the second argument we need an object so we need the API version to be 30 or 8 16. just enter whatever it suggests to you okay then uh for we just enable the type script the type uh typescript flag typescript set it to true okay so now uh go under go back to your slash API slash stripe.ts and here we'll work on the actual route so we'll just export a async async function called get choose to get get request right so we'll do a try catch block first okay so the first thing in the try catch block is uh let's just get the user session so concession it used to await get off session okay so if there is no session.user right that means they're unauthorized to hit this route it's gonna return a next response so new next response we'll just say an authorize pass in the status off for one so the next thing we need to do is actually set up the Prisma data model to handle the user subscription so let's come back to our schema.prisma right underneath the question let us set up all the schema so here the model will set up the model to be model user subscription so this user subscription will have a few things so first ID is gonna be a string ID and the default is going to be a CU ID next up we need a user ID right this will be a string and you'll be unique unique okay then we need a stripe customer ID you'll be string right so this ID has to be copied here so this will be a string okay and then you'll be unique and you'll basically map to the name of a stripe underscore class customer ID so what this map does is basically within our database we'll store the column as stripe customer ID but when in when we're accessing the Prisma client we can actually access it through this camel case so it will be snake case and this will be camel case so that's what the mapping does okay then I'll leave here we'll do stripe uh subscription ID it's gonna be a optional string and then here we'll have a unique and the map will put it to stripe underscore subscription ID then we have a stripe price ID it'd be optional string you'll be a map to the name being stripe price ID like this and lastly we just need the stripe current period n so we can see when the when the subscription is gonna end we're gonna map it to the stripe current period n so we save it so make sure everything is spelled right strap current period and subscription ID stripe customer ID okay so everything looks good so now let's push it up to the planet scale database so we'll just do MPX Prisma DB push right so then they'll push up this new table up to the SQL database okay perfect so now let's continue on with the stripe root so come back to your root.ts file there'll be two scenarios right so whenever they hit this road right they can either be trying to cancel their subscription or their trailer be the first time subscribing so at First Take A we're gonna check are they trying to cancel at the billing portal so we just do uh find a subscription first so we do cons user subscription it goes to away Prisma Dot Prisma dot so let us import Prisma to user subscription dot find unique right where the user ID equals to session.user.id okay so if the user subscription exists and the the user subscription dot strip customer ID exists that means they have already paid before that means they're trying to manage their subscription then that means you want to basically uh create a new stripe section for them to manage their account so we do construct session equals to await stripe so this strap is from our library lib dot billing poor toe dot sessions dot create right inside this query will have a customer being the user sub scription dot stripe customer ID and then the return URL right this return URL is gonna be either be a settings URL so we want to return URL to go to the settings here so this setting will be the slash settings so what I can do here is I'm gonna come around here so for this uh settings URL I'm gonna do process.env dot um if you come in the EnV we can see that we actually have a we can add a event we called Next auth Dash underscore URL let's say that to http conscious localhost 3000 right I will save it so because they want a absolute path so we have to do next of sash URL right next RCS URL plus slash settings so this make sure is an absolute root okay so if they ever cancel or they want to return you'll return them back to the settings URL so after creating a strap session we can just return a next response dot Json uh pass in the URL Bing stripe session dot URL Okay so here is if you're trying to cancel the billing portal okay but if not right if the user subscription doesn't exist that means these users first time subscribing okay so let's create a stripe uh stripe session for them equals to await stripe so check out check out those sessions dot create right the success URL is going to be the settings URL right the cancel URL is also going to be at the settings URL it doesn't matter then for the payment method type payments method types we can only accept a card okay then for the mode you'll be a subscription mode okay if now we need the billing address collection to be Auto uh the customer email it's gonna be session.user.email so if this doesn't exist we'll just pass it as empty string okay then here we're passing the line items it's going to be an array so it's only one object so this object will contain the price price data so this price data will have a currency of USD product data so this productivity will be not object nested in here so the name will just be learning the pro right then the description is Unlimited course generation okay so after outside of the product data we'll give you the unique amount of 2000 visual map to 20 USD underneath the unit amount we can put recurring right we want to basically uh make them pay every month so we subscribe to them every month okay then basically outside of the uh this price data so you see this price data out here right outside here we actually want to pass in the quantity of one so allow them to buy one subscription at a time then the most important then the most important thing is outside of these line items right right so now we are seeing the striped session we're going to pass in the metadata as the user ID equals to session dot user ID user to ID so this is important because later on right later on when we get a web hook right so after we have after the user has paid at stripe stripe will send a web hook back to our API along with this user ID so we know who who exactly paid then we can create a user subscription within the database and update their account so at the end of all that we just want to return next response dot Json the URL being stripe session dot URL so let's uh let's bring this down one layer and let's save it and that's pretty much it then in case there's an error right we can just console.log out there's a stripe there's a strike error okay and I'm just passing the arrow then we're just going to return our new next response just pass it as internal server error we just stated off 500. okay so let's save this so now it's now connected to our front end here okay this upgrade button so if you come out to the if you come out to remember the subscription action here this button right on click what you want to do we're gonna do handle subscribe so this function here let's define it cons handle subscribe it's gonna be a async async function right so this uh async function is going to basically hit our strap end point and then we'll set it uh we'll just redirect them to stripe so we'll do uh we need a loading state right so let's just do cones loading and set loading will be react you'll stay false so this button will be disabled when it's loading okay so to indicate that something's happening to them so uh this will be handle subscribe okay so here let us do set loading equal to true then we have a try catch block and then finally finally we'll set loading to false okay we're gonna do try we'll get conch response equals to await axials dot get slash API strike so this is going to hit our strap endpoint in this root.ts and remember it's going to return a URL so if you turn either will turn the URL to the billing page or return the URL to the checkout page so with that we can actually do a window dot location URL so this this URL comes from this URL field here which is a stripe session here okay and then other than that if there's an error we just control the log error and let's try the error okay so let's save this so by right when we click on this button it's gonna lead us to the stripe checkout so I can press upgrade so it's uh disabled because it's loading in so then we hit the stripe endpoint and it will gonna return us with the URL then we're gonna redirect them to the URL to the stripe checkout so you might need to restart your next year server whenever you change your Prisma database so let's try again so I'm going to hit upgrade so it's gonna basically try to hit the endpoint it's gonna get the URL and it's gonna redirect us to the stripe endpoint so it's loading and loading now then when it's done and we can now see that it redirect us to the stripe checkout page so here we can see that it's charging 13 to Learning Journey Pro right and then we can fill up our fake card information for two four two four two four two four two four two and then the same thing here give a random name and then subscribe okay so now the issue is after we subscribe how do we actually know uh which user actually subscribe to our thing right this is where the web hooks come in so right now we can see that you just browse the settings page but the thing is we have no idea which user subscribe right we have no idea when they subscribe or not right so let's set up the web hook feature here okay so underneath the directory underneath the slash uh API API folder called webhook inside there will create a root dot TS okay so let me just uh export an async function called post right if you're taking the request as a request okay so the first thing is we'll get the body it goes to a weight request dot text okay and then we'll get the signature as constant signature equals to heaters right these heaters will be imported from uh disabled we can do heaters from next next slash heaters so he just dot get stripe Dash seek nature s string and let's initialize our event to be stripe stripe dot event okay and now we can try this we're gonna do a try catch block here we'll try to set even to be striped from our Library dot web hooks dot construct event right so it's gonna take in three things the body the sick nature signature and also a process.env DOT stripe web hook secret so I'll show you how to get the webhoot secret later right so for now let's just continue on first signature so this event so if you catch any errors right we just want to just return a new uh next response right it says webhook error right we just say this off 400 okay so below this try cash block now create a new session so const session equals to events.data dot object as stripe dot check out dot session so with this session right so if a new subscription has been created right how do we check for that we do if events DOT type equals to checkout.session.com completed so make sure the spelling is exactly right on this so if they completed a checkout session we shouldn't do a con subscription equals to await stripe dot subscriptions dot retrieve uh the substation dot subscriptions as string okay so session Dot uh sub scription dot string okay and then underneath here let us just uh the con subscription right so we can just do if not session dot U dot metadata dot user ID so remove this metadata user ID this is what is being passed in from this metadata user ID here right so this is how we retrieve the user ID so uh if not master data dot user ID right we want to return a new next response says web hook error no user ID 400 and then basically if the user ID exists then we can just do await Prisma dot user subscription so we create a new subscription row right the data is going to have the user ID being the session dot metadata dot user ID okay then the stripe custom stripe subscription ID the stripe customer ID equal to be subscription Dot customer s string okay then the stripe price ID will be subscription dot items dot data index 0.price.id and then follow stripe current period n you'll be a new date right we put in a subscription dot current period n times 1000 okay so this is what happens if there's a new subscription being added okay but what if they are trying to uh they got an invoice payment succeeded so we can do if events dot uh type equals to invoice dot payment automatically triggered and they paid once more we can get the subscription equals to await stripe dot subscriptions dot retrieve right session dot subscription as string and then underneath here we can do await Prisma Dot user subscription to update right we can update where where the user subscription sorry we can update our stripe subscription ID equals to the subscription.id and then we're going to pass in the data to be the stripe price ID equals to the subscription to items dot data index zero dot price dot ID okay so let's see why this error here okay so this subscription here uh so this will come from here so substitution let's just paste it here okay then for the stripe current period and this will be a new date right where we have the subscription dot current uh script subscribe uh what's up what's it called uh what's up script send uh new date how do I get this question dot current period n right times 1000 okay and then at the end of all that here we can just return a new next response of now and the status of 200. so this is very important right because we need to tell the web Port that we finished processing a return status 200. if we never return a status 200 the web who will keep continue spamming your endpoint and then you'll never get back a correct response so it's very important to have a status 200 here so now to actually test the web hook come back to your stripe dashboard okay come down to your developer page and click on web hooks so we're going to be testing in a local environment right so the first step here is to actually download the CLI so if you are on Windows or Mac or whatever you can follow the instructions here for me I did Brew install so after you have the stripe command line let us uh just open the command line and I'm gonna just type a stripe login so I'm going to press enter to open it in the browser so to make sure that everything is correct right angle press allow access so I'm gonna log in in the CLI right The Next Step here we can see is Log In The Next Step here is to type in stripe listen forward there's two localhost 3000 slash API says webhook so this uh this uh URL here will map to whatever and point you have in this root.ts file this webhook file single press enter and we can now see that it has given us this assigning secret so we can see that it's completed also so I'm going to copy this whs all the way to the end and come down to my normal energy strike by hook secret so come down to my EnV will do stripe web hook Secret we'll paste it in here so this is from the terminal here okay we'll save it okay so now that we have set up the web hook we can come back to our Learning Journey picture and let's actually try to uh do the strap checkout okay so keep a close eye to the command line here as we do the checkout so come down to the create course page here I'm gonna try to upgrade my account to this track so I'm going to press upgrade right it's gonna then uh redirect me to the stripe check-in page okay and let us enter the details here so uh four two four two four two four two four two four two four two okay just put my name and let's press subscribe so if everything goes well we can see that webhook will be triggered and then a news user subscription will be added so you can see everything is working well right here everything is uh been triggered and caught right so now everything is working and all look the status is 200 so that means it's good here it's good so the reason why I have this 500 is because probably because we have we don't have this settings page being properly done so if you actually come down to another terminal let's do MPX Prisma Studio okay let's look at whether the user subscription table has been added so if you come down here we can see a user new user subscription a row has been added my user ID is here and also the current period end is added here so that's good that means everything is working fine so let's create this settings page right now okay I'm gonna close down everything here so underneath the app directory let's come down to app and create a new a root code settings and have a page dot TSX tsrefc so this way settings page okay so let's wait for you my company is lagging a lot so we have this settings page so right now this settings page if you've seen the original demo the settings page will show whether I'm a pro user or whether I'm a normal user right so let me create a utility function to basically check whether I'm a pro user or not so I'm gonna come down to my slash lip I'm gonna do subscription.ts okay so this file is gonna just uh tell me whether I'm a pro member or not so either here I'm gonna export uh the first thing here I'm gonna have is the first thing here I'm gonna have is a constant Cloud day in millisecond which is going to be 1000 times 60. uh times 60 times 24. so this is how many milliseconds are in one day then we'll export a async function cons uh check subscription equals to async function right so this will return us with a Boolean right so we're gonna get the session first so concession equals to a weight gets off session so if there is no session.user then obviously we're not log in therefore we're not even a pro member so just return false however now let's try to check for the user subscription click 0 sub script version equals to await Prisma dot user sub scription dot find you unique okay so where the user ID session.user.id okay so if the user subscription uh does not exist we can just return false again because that means they're not sub because that means they're not subscribed however just because it existed I mean there are also many pro why if they expire it right so we do coins is valid equals to user subscription dot stripe price ID and that the user subscription dot stripe current period n don't get the time right plus day in millisecond right is bigger than date not now so let me want you to what we just did so this value contains two statements the first is that the user subscription must have a strike price ID on it and the second thing is the current period n plus one day in milliseconds so we give them 1D buffer right so if the the current period end is uh bigger than data now that means that they are pro members and the subsection has not expired uh a double bang is valid so this double bank will just do the download negation to convert this into a Boolean I'm Gonna Save it okay now that we got this track subscription utility function we can now go back to our uh settings page okay so this settings page is gonna be a server async component right we're gonna just check its Pro so are they a pro member right we're gonna do that await get a check subscription a function that we've written just now right so I've got return I'm gonna return a div with a class name of padding y of eight MX Auto Max width of 7xl okay and then underneath the div does have a H1 to say settings we'll give a custom of text 3XL font of both unlimited H1 let us check if it's a pro user so if it's a pro user I'm gonna return a P tag it's gonna see uh you are a pro user we give a costume of text Excel text second uh secondary Dash foreground divided by 60. so if they're not a pro user I'm just gonna give you a paragraph tag that says you are a free user uh we have the same class name out here so the copy in here copy paste I'll save it and right now the setting space it can show me that I'm a free user I'm gonna Pro user because I just need the subscription therefore it's gonna show me I'm a pro user so down here I'm gonna create another component called subscription sorry subscription button right let's create a new component for that so component subscription button dot TSX tsrafce right so I'm gonna save this I'm gonna import it in this other file okay so the subscription button will basically lead us to the stripe checkout page or the stripe billing page well passing the is pro uh prop here is pro so we're gonna have to accept it in here so we'll just do within the props let us handle it right so each Pro will be a Boolean okay then with that we can destructure it from the props itself is Pro I will save it and right now we can see now it will load it in so with this subscription button we want to basically make it into a client component okay so I'm going to come up here making the use client directory right so we're gonna have a loading State and a set loading okay so let's return a very simple link it's just a button right and then we'll just want to show that if it's Pro right we're gonna show as manage subscriptions otherwise we just want to say upgrade and then we'll save it uh let's see okay so this should be a question mark we'll save it so give this button a class name of margin top of four okay sorry margin we'll give this button a class name of margin for four and also we're going to disable the button while it's loading okay so when we click on the button we want to Handle Sub uh script subscribe so let's create that function so come out here and do cons handle subscribe it goes to a or async async function right it's gonna be very similar to the button just now we're gonna do set loading to be true okay then we're gonna do a try cache block then finally I'm gonna just set the loading to be false so initially I'm gonna set the loading with two then we'll do crunch response equals to await axios right impactuals.get slash API says track so then we can set window.location dot href equals to response.data dot URL so we're hitting the same endpoint and just redirect us to the strike page then if there's an error we'll just console.log uh billing error did I save it so here this is gonna be handle what's up subscribe okay then go just save it and wait for next years to compile and reload so my code is getting a bit slow so now this button is working so if you click on this menu subscription it's gonna hit the end point this APS extract it's gonna check that we already subscribed so it's gonna then bring us to the billing page so right now we can see that there's gonna be an error right he's gonna let's look at the error what does it say so he's gonna he's gonna tell us that uh if it's cool okay so the message of the error is you can create a portal section in this test mode until you save your uh customer portal settings so we need to basically come to this URL so you go to your dashboard so you need to come to this URL so just open this it's under the dashboard that's dot stripe test settings slash building says portal right and underneath here all you need to do is just um activate the billing portal so just press activate test link and then everything will be fine so now if you come back here uh to our localhost and press manage subscription this time it will then redirect us to the billing page so I don't think there'll be an error anymore okay so that's good so we can see now that it has brought us to the billing page and right here is where we can manage our subscription we can then either cancel the plan or do anything else so that's on your customer side okay so that's perfect everything is going well so let's come back here and that's pretty much it okay we're doing extremely well we're almost finished so we have now the stripe integration done so let's go back to the create course page so if we are at least subscribed to if you are the pro member right we actually do not want to show this uh three generations right because I'm a pro member I should know that I have unlimited Generations so I'm gonna come back to our create uh create course form right so how do we actually get whether we are a pro member so we're gonna have to go up one more level up in the component tree so let's go to the slash create uh page.tsx So within here I'm gonna just get the cons is pro equals to await check subscription a function and then I'll do some prop drilling by passing into this create course form so each Pro equals to is Pro we'll save it we'll come to create cost form we'll accept this in the props so it's Pro will be a Boolean we will extract it into the props is Pro then underneath here finally we can uh show it right so come down here and we'll show so if I'm not a pro member if I'm not a pro member then it makes sense for me to show this subscription action whereas if I'm a pro member then this should not show which is working fine now so the last step of the integration is to come to my uh this is a root slash create chapters so I'm here right I want to first check if they have enough credits so what I'm gonna do is I'm gonna do cons uh call session right I'll get the user session cons session equals to await gets off session so if I do have a session dot user right I've got a return a new next response you can see unauthorized and also rice but if the user exists let us check the subscription so it's Pro equals to await check subscription right so we get a first check right if the session dot user dot credits is less than or equal to zero that means they have run out of credits I'm gonna tell them uh they have no credits left right but this is also true if they're also not a pro member so if they are pro member right this will become false and then this whole thing will become false right so if there are not a pro member so if they are free member and their credit is below zero then we will just tell them their own credits right but the thing is if they are pro member then this entire thing will just be uh skipped over right so that's how we do it and then all the way at the end right after we created all the chapters before we do the new next response we can just do await pushma to user dot update right where uh ID equals to session.user.id so we're trying to decrement their uh sorry which one two data you just want to decrement their credits right credits credits Toronto documented by one so every time they create a new cost we just document that crisis by one and then it will show up in the cart okay we're doing excellence and we are basically almost done with the project so just before I deploy I just add one more thing so right now if you even up that's right if I refresh the page unless I click into this right I'm gonna click and watch the delay click you see there's a delay do you see the delay if I click on this there's a delay of about one second before it shows right and basically there's a bad user experience because once the user click on the link they expect to be navigated immediately so if they don't see it being navigated immediately they might find it to be a bad user experience because they don't know what happened right so the reason why there's even a delay is because Nexus 13 is a server side rendering right so when you click on this page it's waiting is waiting for the server to finish rendering the HTML before returning it that's why there's a delay so how do we show that uh show a loading indicator okay so all you need to come to do is come to your folder directory okay come down to your let's say uh for your for your each individual routes like create uh create create chapter okay I want you to create a new low loading.tsx so now here is under your crate here a loading loading.ts X so this loading your TSX is a special file right this file will be this component inside here will be shown whenever uh react whenever next year is actually rendering out the page right so if I do tesrafc right I'm gonna just put it as loading page okay and then this loading page will be this loading uh page or this loading page or should I name it as loading component this will be shown whenever is streaming in the data the founder just returned a a div okay actually let me just move this all to the back component okay and this div will be absolutely positioned in the center so give your custom off absolute top of half left of half negative translate X of half negative Translate Y Translate Y of half so we position it absolutely in the center okay and then within this div within this div I'm gonna return a a loader to and I'll give you a custom of animate spin so that it will be in the center and it will be spinning okay so let me just check uh okay so let me make it a little bigger here so we'll give you a wide width of hand and height of 10. okay now if I save it right and this building here is right now is under slash create slash created right the modify that means if I click on this create course you can see there's a loading spinner you see the loading spinner before it loads in the data so that's good that means it's working and all you do is I'm gonna copy this loading.tsx into all the files so copy these into here okay now copy into Gallery I'll copy into the settings page I'll copy into this a slug page and you can just copy into anywhere that is showing some UI so now if I go to any page like here you can see the loading loading spinner before it deals the data so at least now the navigation is instant and the user knows that something's loading the background okay so that's good all right so we are done with the project uh good job if you're following up so far and so now I'll teach you how to deploy to a virtual private server instance okay so the reason why as I said in the beginning we don't deploy to resell right because versal has a function execution time of five seconds maximum so if you go to the Versa pricing okay and let's go to the pricing for The Hobbit here okay Hobby you're gonna have to if you scroll down and you find look at this server serverless function execution timeout meaning right how long does your how long does it allow your API route to run before it times up in this case on the free Chase only 10 seconds right but the thing is as you have tested in our local environment the open AI API takes more than 10 seconds to run and return the result right therefore if you try to run it on this a versaille feature it's gonna time out and you're not able to run your project right the only way to do it is if you're going to upgrade into a pro account then you get a 60 second timeout limit okay so you can either you can Define yourself but then today I'll be showing you how to actually do proper deployment on your own virtual private server right and then I'll even teach you how to set up GitHub actions for a automatic deployment CI CD upon your code push and also teach you how to link out your domain name with SSL set to the VPS such that you're able to come to your custom domain name right so learn journey.relevant.tech and be able to self-force your next GS project okay so uh for this to work right I need to assume that you have a domain name already so you own a domain name and you have a control panel to set up your your custom DNS okay so the first step to actually deploying to a VPS is to actually set up the VPS so I'll be using digital ocean for this right so come to your digitalocean console so you need to log in or sign up but the good thing about digital ocean is that it provides you with a 200 free credits when you first find out first sign up with it so you're able to use that credits to actually follow along with this tutorial okay so log in and sign up with whatever account and so when you come down to your control panel you can learn your projects right and so the first thing here is you need to add a new project so come down here and you project and I'll just name it uh learning Dash Journey Dash YouTube and actually let me expand my Firefox okay so name it whatever you want and you can add description and add purpose like um in this case we are hosting a web application then click on create project now it's basically asking us even the most resources we'll skip that because we want to create a new uh basically a new compute instance okay so now uh welcome to digital ocean we want to spin up a droplet so droplets are just a virtual machine that anyone can set up so basically you'll be deploying on a new Ubuntu server so I'm going to choose a place that's nearest to me which is in Singapore then uh so you choose Ubuntu as the image choose the latest long-term support version LTS 22.04 and for the droplet type you can just choose uh something that's within your price range I'm just gonna leave it as the default uh you can host it as anything you want right it says let me just put you on this 14 per month offer and then I mean because you will get 200 free credit so this one you can you don't have to worry about it exiting the cost and then for the authentication method you can just choose uh whether you're going to create a new SSH key or password in this case I'm just choose the password connection field and enter your root password so make sure you remember this I'm gonna just enter it as um let me see can I have a secure password so I need to have eight characters along one upper case one number okay I'm just gonna hold it as one two three four p a s s okay let's hope that we can remember these together one two three four capital p e s s okay so then after that everything is and then that is fine okay um let me see one droplet and the host name for the host name just give it something that you would remember like learning Dash Journey and then uh that's it then you can just create the droplet okay so you need to remember the the password you set for the root okay save it oh don't save okay so right now it's spinning up the droplet you can see the the progress bar coming up so I'm gonna keep refreshing it until it has finished uh finished initializing okay so while it's initializing I'm gonna want to set up uh the docker file within XCS okay so uh right now if you call if you come to our our browser Okay the reason why right now right the reason why people deploy to resell is because Versa hosts the next year's project for you right but today I'm going to teach you how to actually host the next GS or yourself in your own computer so basically what we're gonna do is come to you can just search for uh South holes next js on Docker right so if you're not sure how to use Docker you can just follow along or teach you everything there is to know so come to the first link on the next year's application so next just basically has a command for you to do next build right we should build the server for you and then you can just do MPX Run start and then it will run the production server for you okay so you can read the documentation right but usually people deploy with first cell because it's managed and result does all the hard things for you but today we're gonna teach you how to do self-hosting right so I'm gonna come down to the docker image file so first thing is you must have Docker installed so in this case we'll be installing Docker on the digital ocean droplet and then we'll clone the with soccer example all right so let me just open this with Docker repo okay and then all you need to do is come down to the docker file within that repo and then I'm going to copy the entire thing here come back to your vs code okay in the root directory I'm going to create a new file called Docker file right capital D and there's no extension okay then copy in while we have copied within this with Docker repository Dockers file and copy in here okay and save it so basically all this does is it will create a new node.js uh computer and then it will just copy over your package Json then it will basically run a npm run build to actually build up the next year's server and then it will just run the next shares then you export part 33 000. so you can just see that all it does is it will do node server.js which will run the next GS server so that's the docker file okay so let me just post this entire repo up to uh the GitHub repo so I'm going to come down to my GitHub come down to github.com new so we can create a new repository on GitHub and push it up there okay so the reason why we are pushing up here is because we need to be able to pull the code repo into the digitalocean computer right so just name it a Learning Journey Journey that's YouTube uh plus YouTube okay and you can give a description if you want a public it and then just do create repository okay so to post uh it up there we just need to do first is you need to come down to your vs code come down to your vs code okay open your terminal I'm gonna stop the server for now okay I'm gonna stop this web hook also and we can just do git status so we just need to add all changes so far into the repo right except that we don't want to add this dot EnV file because we have pushing it to a public Repository so come down to your git ignore and just add the dot get a DOT EnV file such that the EnV file will be not by git so if you don't get status clicking you can now see that the EnV is removed from the repo so now this thing you do uh git add dot so add all the files get commit M initial comment and then all you do is come back to your repo we need to add this uh remote branch get remote at origin and then get push Dash U origin mean so this will push up our local git repo to the GitHub repo so now if I refresh the page I can see all my my files are up here in this Repository okay so that's good so now all you do is uh come down to your digital ocean and let's connect to this droplet what I'm gonna do is so click into the digital ocean and press enable now for the reserve IP because we want a stable uh external IP so that the IP doesn't change every few days and then that will cause our DNS to basically lead us to a 404 error okay so press assign reserved IP and basically it will just reserve a static IP for us so now this is our permanent external IP so note this down okay so with this a reserve IP now we're gonna basically connect to the local computer so let me draw out the diagram for you so that we have a better understanding of what's Happening okay so right here I have um my MacBook so this is my local computer which I'm doing the development on right so it's what you have been following on with okay so now we just did a digital ocean that means within the cloud there is a digital ocean a virtual private server so it's basically an Ubuntu computer that's living somewhere else in the cloud in this case we set the location to be in Singapore okay so right now we have pushed our MacBook code we have pushed the local code to our GitHub right so the code our code now lives on GitHub so now what we're gonna do is we're gonna basically go into this uh we're gonna Connect into this VPS right we're gonna connect it through what they call SSH SSH secure shell that means we can basically connect to the Via terminal and run commands on the terminal here then we're going to run the commands on the terminal to basically pull down the GitHub code onto this right we'll do git pull or git clone so now the code leaves on here right and then we can then run the docker file so that we can start up the server okay so to ssh in or open your terminal okay and do and remember your public IP address this copy it and then do SSH root add paste it in here okay so let me just let me paste it in here okay so root is the username of the Ubuntu computer and this is the public IP of the Ubuntu computer then you just press enter okay and then you ask for your password so before this you actually it might ask for your uh in my ass if you want to trust the authenticity of this host right you just have to press yes to make sure that uh you're connecting to it and then after that you can just come down and enter the password so just now we did p uh uh P what do you do one two three four capital p a s s so enter the password that you set up and now we are connected here so basically now we are connected to the computer here and we can run commands here so the first thing is to pull down the code from GitHub so what I'm gonna do is I'm going to come down here so we're gonna be basically pulling it using SSH but the thing is right now it's gonna fail so if you copy the SSH uh link and we do let's just do a git clone and we're pasting the SSH link it's gonna tell us that it doesn't have the you might not have the access right right because uh the GitHub doesn't know about this digital ocean and it doesn't know that it can be trusted so what I need to do is actually come down here and create a new SSH key so I can do uh so come now here come down to Google and we're gonna search set up SSH key okay so we need to run a very simple command which is this uh SSH key gen so let me just search from the SSH Keygen command then you can run it together okay so we just follow this um so we copy this command down here and basically what this does is Will generate a new accessory key pair on a computer and then we can copy the SSH key onto GitHub so that GitHub will be able to trust our computer so copies command and then just run it so it should be generating a public private rasrse key pair okay so wait for you to generate and then BCU will have a private public key okay so let me just let's see if I can visualize it out for you so after running the SSH key gen I will have a ID RSA dot Pub right so this is the public key and I also have another private key which is just ID RSA okay so this is my private key so later after it has finished generating right uh we'll just leave it at the default so you can see that it's generating the key at dot SSH slash ID RSA just leave it a default and just leave the path phrases empty okay so right now if you list and we go to cd.ssh right and release it again we can see there's two files idrsa which is our public key uh no this is our private key and idrsa Dot Pub which is our private key okay so we have this private key and public key what we could do is copy this public key up to GitHub so that this tools uh com these two computers are linked and then I can do git clone to clone the GitHub repo down using my SSH method so we have this IDs rsc.pump what I'm going to do is I'm gonna just do cat ID r s a DOT pump so I'm going to copy this a public key right I'm gonna copy it down all the way down here copy and then we'll come out to my repo GitHub go down to your uh go down to settings under your profile scroll down and you should see SSH and gbg keys okay so then add a new SSH key name it a digital ocean Learning Journey Dash YouTube and we have to choose it as an authentication key then we can see that we can copy in the public key in here okay then just press SSH then you basically need to uh enable your thing to the use your password so I'm going to enter my just my GitHub password Here okay so now we have successfully added our digital ocean so now both of them are linked and now I can do git clone again with no issue so I'm gonna CD back into my original I'll go up all the way to my GitHub clone sorry I'm keep cloning to my SSH method so hopefully now with the key pass link I can successfully clone it and it works now so we're able to link it up okay so the reason why I set up all these SSH thing right is because later for my GitHub actions CI CD pipeline I will require the thing to automatically pull it down therefore it's good that we have set up the SSC key for that so all you need to do now is we can see that we've cloned Down The Learning Journey that's YouTube on our digital ocean machine into that okay I'm gonna do and because we have the docker file here right we have the docker file here right we need to download Docker so fitting video download is sudo apt in uh so just do sudo app update first so update all the repositories within the APG command and then we can do so apps install Docker and Docker Dash compose okay so push press yes it's gonna try to install extra space okay so then we need to set up a Docker compose file okay so follow along a Docker compost tile will just basically allow us to run the docker let me just press ok so let's set up the docker combo file so come down to your vs code again so come down to your repository and then basically on the same level of the docker file you have to create a Docker Dash compose dot yml.yaml okay so with this Docker compose dot yaml right let's type in the docker compose command so this Docker compose command will basically run out Docker file so we specify it's version three okay version three we have we can basically list a bunch of services but in this case we only have one next year's service so we can just uh tell you that we only have this next year's service uh the build path is just dot uh for the build path we're telling it that where is the docker file at the docker file is in this local dot path right so you look for DOT uh Docker file which you will find it here so this is where the docker file lives and then just don't come here you can tell it that Docker file exists at this Docker file or container so with these two lines you'll tell it where to locate the docker file then underneath uh the build on the same level as built you do the list of ports as uh three thousand mapped to 3000. so it's basically saying that within the docker I'm gonna the next service can run on Port 3000 I want to expose it to the outside world on the port 3000 so we're doing a port mapping sugar save it okay and I'm gonna push this Docker file up so just come down here and do a git add dot get commit uh at Docker compose get push so you push up to the repo and then we can go back to our digital ocean on our terminal and let us do get pull again so it'll pull up the he'll pull out the docker compost file okay so you can see now we got the docker compost yaml okay okay so now you let the docker compost or yaml let's actually try running the docker file okay so to do that you're gonna run Docker uh compost uh up uh sorry that's just build Dash D up okay so because we have installed the docker compose commodity it will build this uh Docker file and then it will detach it from the terminal and it will go up so let's try running this uh okay so it will say that the command is not right because I think is uh okay so I think the command is Docker compose up build and Dash D maybe okay so yeah so I just need to put the arguments after the docker compose up command so right now it is basically looking at our you see this step one from node 18 Dash Alpine this is what is specified in the docker file see there's no 18 Alpine so just running all the commands in this Docker file and building the computer for us so you can imagine this Docker as just like a mini virtual machine that is being run up okay so let me visualize it out in actually draw so right now within our digital uh VPS right let me just explain it up for you okay so within this digital VPS we have spun up a Docker container this Docker container is going to follow the commands in the docker file right and then it's gonna run a nexjs application for us so this Docker container is a mini virtual machine that lives within our VPS and it's gonna have a nexjs application running this next.js application you can expose the port of 3000 okay so right now it's running on localhost 3000 but how do we access it from from outside right because right now you can see that this port 3000 is exposed within the container but we cannot access it from outside so to basically link up uh from this next year's from inside to outside right we're going to expose it to the external IP so remember that we have external IP here so if we uh if this is the location of the droplet from outside the world to access it at this IP we need to basically install use another2 called nginx this ngx is basically a web server so basically it will help us link up the internal Port 3000 to the external IP such that you're able to access this external IP then you'll be the access the internal nextgiers application so I'll run you through how to do it step by step so don't worry about it I just want to show you that uh I just want to explain the visuals for you so we are basically waiting for the docker to run okay Docker to run so right now is just we can see that it's running mpm install npm install and then it's just uh creating an optimized production build so what it's doing is uh it's coming to your npm scripts we can see that it's running the npm Ram build and the npm Run start Okay so right now we can see that it says Uh there's something off right it's because uh you need to have another additional check here so the first thing here is to come down to our next Dash config.js and under eslint you can put ignore during builds to be true so we're going to ignore EX in when we're building and so for typescript typescript flag we can ignore build errors also true so this is just purely for to avoid W errors if and then one last thing is I forgot almost forgot come down to your package.json and for the build company to modify it to basically add the Prisma generate so you have to do MPX Prisma generate and then you can run next build okay so let's save this okay so one last thing I actually forgot is that you can see that now there's an error saying that the environment variable is not found database URL of course it's not found because we don't have the EnV file here remember we didn't push the dot EnV file up to GitHub therefore it doesn't exist on this machine right so in this picture you will say that okay on my MacBook there exists a file called dot EnV right there's a DOT EnV file here but we didn't push it up to the repo therefore within this project I do have the EnV file so a very simple fix is just come down to your vs code EnV just command R so select everything come back to your thing and just within your repository you can just type uh just open your your text editor in this case I'm gonna put vim.env so I'll just open up EnV then just press just paste it in then to exit Vim type Escape colon w q okay so then we have a new DOT EnV file uh reading here so now we have the EnV the dot Envy uh the envy that is uh in the digitalocean droplet okay so one more thing so after you have created your Docker compost file and you push it up to the repo there's actually one more thing that we need to do right if you if you come down to the uh let me see if I can find it you come down to your uh Docker instruction on how to self-host next year's right you come down to it there's one more instruction that we need to follow which is to we need to add this uh output Standalone config to our next config.js okay so we have to come down to our vs code come down to your next config again right so here under output output output right there's an additional option for Standalone right uh sorry output equals to Standalone right this is just the output just to be stand alone so you save it so this line is required for you to actually run the host the next GS server on a Docker image okay so just push this up one more time so next config change push up the changes and then after you push up come back to your digital ocean SSH client and then finally you can do get pull so now we have the docker compose file and the output Standalone then the last thing we can just do Docker Dash compose up that says build Dash T so this will basically look at our Docker file it's gonna follow whatever instruction we have within this Docker file it's going to run the next shares project for us and then it's gonna basically detach it from the terminal okay so let's just run it and then we'll basically see the uh the commands being run each run out are running a build Command right MPX Christmas generic and next build right which is why we're specified in the package Json uh sorry package Json um build command so mph generate and next build so that's exactly what is running here January and the next build so it's just building up the project and then creating optimized production build and then finally it will run it on Port 3000 okay and then now we can see that our next Productions project has successfully run so it's done and so because we detach it from the terminal if you type Docker PS we can see all the containers running in this case we have a container ID of this and the ports you can see that let me just zoom out so we can see that the image is this next CS project and the port is 3000 to 3000 meaning if you do Crow Local Host at 3000 we can see that it returns us to the next JS uh HTML code so that means it's good that it's running right but the issue right now is as described earlier right let me close this all as described earlier it's only running on this localhost 3000 how do we expose it to the public IP so that we are able to access it because right now if I copy this IP and come up to the uh my browser you can see that it's not returning anything so here's where nginx comes in so the first thing you do is come down to you have to sudo apps install engine X okay so you will install the ngx and basically this ngx acts as a web web server such that you can handle the the you will handle the traffic coming in uh to our computer and Link it up with our localhost 3000 okay so let's wait for you to install so press ok so after installing after installing nginx right we can do sudo service nginx status right so you tell us that ngx is right now running so let's look at the effects so if I come down to my uh let's copy your what's it called your public IP and then come to your web browser and type http right HTTP colon the IP that you have copied we can now see that we can there's a new screen that says welcome to nginx right and basically this is running on HTTP Port 80. so this is the same same thing as typing in uh Port 80 it will bring us to the default and internet screen so what's happening right now is we are basically accessing we are going to activate basically trying to access uh this uh computer right we're accessing this computer at Port 80. so Port 80 uh for Ed right with this IP address right and basically what engine access was that njx is able to listen on this port so ngx comes in here so let me put NJ njs listens here and then it serves out the default home screen for them so what we're going to do is when we listen to Port 80 right instead of letting nginx serve out the HTML content we're gonna basically ask nginx to Route the traffic to in the internal localhost 3000 with that we are able to serve out the next year's project and we can see the link so the external uh website is gonna request for Port 80. ngx is gonna then route the traffic to Nexus 13 the next yes project then you return the response back to the server and then we'll be able to see our next year's project running on this IP address so let's do that so come back to your thing here and you have to do uh clear and then you have to CD so we are in the home directory all you do is to do a CD slash Etc right slash engine X right if you list it again we can we can now see we're in the ngx configuration folders I'm going to go into CD sites and a size enabled right and then uh only to do Vim default so we're gonna edit the default file so right here we can see that this is what the server looks like it's going to be listening on Port 80 as the default server right so you have to worry about the configuration you just need to know that look what is the what it does is it listens to Port 80 and it serves out the HTML files for you and that's that is what is being shown in the home page when you visit HTTP uh when you visit the home page so what I'm gonna do is here is I'm just gonna delete this entire file okay I'm gonna delete this entire file so I'm using Vim within this so you can use something you have more familiar with like Nano so I'm just gonna delete the entire file okay and I'm going to type out the configuration one by one so the first thing here is I'm going to press server so I'm gonna open the new server block okay within this new server block I will have a server name the server name is gonna be the the public IP address so I'm gonna paste in here okay this is the the server name uh there shouldn't be any space here let me try this like this okay and then there's a semicolon then after the server name I'll have put location set so what this location means is that uh whenever I visit this public IP address the home page the Home Route I'm going to just redirect the traffic to our next GS project so remember we the next year's project is running on HTTP colon such as localhost 3000 so it's running within this container right so what we are basically doing is we are linking up the port 80 to Route the traffic to the next CS project okay so with this I can just press colon WQ and then we can do sudo uh nginx Dash T right so this t flat will basically test the syntax and make sure that our ngx config is correct right and everything is correct so then if everything is fine I'm gonna do sudo service nginx restart so we're going to restart the Android server and next if I go back to my browser and I refresh the page then now you can see that on this port 80 we can now successfully see that our next yes project is running project is running and hopefully the gallery page will show and yes it's showing so we've successfully hosted it on the thing okay so but we're not done yet so right now we can see that it's running on this ugly IP address and it's it's not https so how do you get the https so the next step is to I want you to come down to your um come down to your domain domain console and in this case I own the domain relevant.tech so I'm just gonna manage my domain so uh just log into your domain so you can use any domain hosting like GoDaddy or Google domains the important thing is that you're able to configure the DNS customly custom uh domain so come down to your DNS management okay so click on manage DNS so you might have to play around and try to search for where your DNS management screen is so I'll be basically configuring our DNS screen to basically route the traffic to the IP address so let me describe to you what we're gonna be doing so right now I own this domain name relevate relu ids.tag okay which is a random domain name I love it so what happened what happens is what we're gonna do now is I'm going to configure the domain name so that whenever someone visits relevate.tech so like someone visits relevate.tech right we're gonna basically route the traffic to this IP address and then with this IP address it's gonna route to Port 80 and then finally it will call indoor nginx it's gonna stand for the next shares project it's going to return all the way back and now the user the user is able to see the Nexus project on this domain name so all we're doing here within the console is linking up this domain to this public IP so come back to your uh connect your domain uh sorry come back to your domain registrar so you have to add a record so it's going to be a very similar thing you just have to find the a record within your domain management so we're going to be adding a new a record so an a record is just basically a mapping from domain name to the IP address so uh go wait for you to load add a record so the host name is gonna be uh whatever name you want so I'm gonna put learning Dash Journey Dash YouTube so this what the domain name is going to be Learning Journey YouTube dot relevant.tech okay and then for the destination IP address I'm going to copy in the public IP address so this public IP address is what is going to be linked up here right we can link it up here so the domain name is gonna route to this public IP address with that you can just press add record So within your own domain management system you can just do something similar so now we can see that we have this new domain name okay it's gonna be mapped to 159.89 so to make sure that it works I'm gonna come down to a website called uh domain propagation right so this website allows me to check the domain propagation and now if I check for learning Dash Journey that's YouTube Dot relevate.tech and I search for the domain we can see that it's all being resolved to this IP address right so you have to wait for a while sometimes uh it takes some time for you to propagate but in this case we have now seen that this URL this domain name properly maps to this IP address which maps to our next yes project so if I copy this domain and I come down the browser and type HTTP so remember we still don't have https we only have HTTP which is running on Port 80. if I type learning Dash Journey there's youtube.relevate.tech hopefully if everything works well okay now we can see that it's running it's working again so we can see that this domain name uh runs on xjs Project so the issue now is we don't have https so let's now obtain the https so come now we're going to be using a third bot nginx so search for search about nginx so we're using the let's encrypt so come down to the this assert board link support.ef and then basically what we do is come down to my HTTP website is running on nginx on Ubuntu version 20. okay so then we'll follow the instruction to set up the history the SSL set so first is SSH Studio server so we are already in the server the remember we access it into the server here right and then we install snapdead so in this case Ubuntu already comes with snapde so we don't have to worry about this step and then remove any support Auto so you have if you've already done this before you can remove it so we're gonna start fresh so all we want to do is install surfboard so I'm going to copy is command I'm gonna paste in here so then I'm gonna wait for you to install right okay so after installing come back here so we have to link up the command so we'll just link it up symbolic link and then lastly we just need to run this command okay so before we run this command I'm gonna go back into uh I'm gonna edit the default file again but this time instead of this server name of this naked IP address I'm gonna put in my Learning Journey Dash youtube.relevate.tech right so this line will tell you that this line will tell me that when I visit this link When I visit this link I want to proxy pass here okay so make sure this uh this link is exactly what you put in your domain DNS management I'm gonna quit and then I'm gonna do paste it in sudo search board that's just nginx so this will help us obtain the SSL set so we'll let it run and it's just asking to enter your email address in case you need to be contacted I'm going to enter in so we press uh accept the terms of conditions we're gonna press no to the mailing okay and now so we can see that it's asking which names would you like to activate https for so in this case it has detected our nginx configuration so I'm gonna press one so we want https for this domain I'm gonna press one to enter and so now it's gonna try to request a certificate for our domain right so wait for a while it's gonna try to hit in this case it says successfully deployed certificate so if you go and uh inspect our nginx configuration again now we can see that it has created a SSL set for us right yes create the SSL assert for us and then the magic here is if you come back here so right now we can see that it is uh there is the secure this connection is not secure but if I refresh it now you can see that https is enabled so now we are https so now this link is production ready and safe for everyone to use okay so that's really good we have now set up the DNS propagation we set up everything else so everything is going perfect so the last few step here is if you press sign in right now it's going to tell us that the domain name is not gonna match there's gonna be an error right we can see that it's going to Route us to localhost 3000. right so what we need to do now is come back to our domain CD and then CD into our project here we need to edit some EnV files so in this case this next author I'll need to change this needs to be changed to hdbs uh his GPS so whatever domain name you have here okay then everything everything else is the same okay and then one more thing is come back to your Google Cloud console right go into your credentials or your credentials here and we need to add the domain name to the API key the client ID so just scroll down so for this URI authorize JavaScript domain we now need to add https colon SS learning there's Journey Dash YouTube Dot relevate.tech and then copy this down and we'll do the same for here but this time you read slash API slash off slash callback slash go please make sure everything works and then just save it okay so we'll save it and now whenever you change the project right it's uh you have to basically rerun the docker compose file so how to rerun it you have to first do Docker PS you have to shut down the container so copy to The Container ID and do Docker stop so you'll stop the container so we can now see the docker is no longer running then because we've edited dot Innovative we have edited the EnV file we will do Docker Dash compose up that's build that's D so we run the same command so now it's going to basically rebuild the entire project uh taking in the new EnV file okay so it's going to be building and stuff so uh after this has built everything is going to work okay so uh the last thing here is let's go to the stripe page so come back to your stripe uh Management console come down to developers come down to webhook right for this web Hook when you add a endpoint Okay so enter your password and the endpoint URL this time is going to be https clone slash a learning Dash Journey Dash YouTube Right dot relevate.tech slash API slash web hook right this was our web hook right if you come down here it was in slash API slash web hook okay so make sure everything is correct and we are entering your https domain name lastly we need to select events to listen to so know what events you will listen to in our web hook right we listen to check out our session.com so let us come down to here we try to activate that so check out the session dot completed go check this and we also listen to invoice or payment succeeded so come down to invoice dot payment doc succeeded here check this and that's pretty much it right so we're only listening to two events press add events then add endpoint then with this endpoint it will give us a signing secret so you need copy this secret come back down to the docker right so because when you edit the EnV file again we'll do the same thing we have to stop the container first Docker stop this then we'll edit the EnV file then for this stripe my whole secret right we'll just delete it and we'll paste in whatever the signing secret here is so we'll paste it in and make sure it's correct so at the end ends with five PC five PC so make sure everything is linked up save it again and the last step is just let's run the docker compose up there's build HD and then we'll save it and then now everything will work perfectly so we've successfully linked up the Prisma we've successfully linked up the stripe it also linked up the Google Authentication okay so that's good so we're gonna wait for it to build and then so everything is going to be complete now we're gonna set up the CI CD pipeline so right now whenever we push right whenever we push the code we can see that we have to go and manually SSH into the server and then run Docker compose Docker stop right these are very tedious process right what I want to do is I have this GitHub repo right so this GitHub repo whenever I detect a new push right I'm gonna set up automated service right I'm gonna set the automated service so that when I press when I detect a new code push whenever I push to the repo they're gonna have an automated sequence to basically SSH into this uh computer it's going to automatically pull in my latest changes it's gonna run Docker stop it's gonna then restart the whole container and then that's it so let us automate that that page right now so to create a new GitHub action come down to your repository here okay you have to come down to your folder and in your base folder let me just zoom out a little bit because I can't really see close out all the files okay so come down to your base folder base folder create a new folder at the base root called dot GitHub so it has to be done GitHub exactly and then within the GitHub folder uh create a new folder called workflows so anything inside this workflows will be a CI CD pipeline so create a new file you can call anything I'm going to call it deploy.yaml so this yaml file is going to describe the automated action that's gonna be run whenever we detect a code push So within this deploy.yaml okay I'm gonna copy in this code okay so I'm gonna leave these uh cicd code in the description later okay but then uh I'm gonna walk you through exactly what we're gonna do here and just line by line so we're gonna name this action as deploy so it's gonna have a name so whenever we push to the main branch right we're gonna run this action okay so we're gonna have a job called production the name is going to be called deployed to digital ocean so you can automate the deployment process to digital ocean he's gonna run on Ubuntu that latest and everyone gonna be on production so later I'll explain why he has to be production so now we can specify the steps on how to actually deploy this app so the first step is we're going to check out the code that means we're gonna pull in the code into the GitHub action and then we're gonna basically set a few environment variables so we need SSH private key so this is the private key of the digital ocean okay then we have the SSH host and username so later on we'll be setting these secrets within the GitHub dashboard so now these are the actual steps to actually deploy so first we're gonna Echo deploy the ec2 to make sure everything is working then we're gonna basically Echo the SSH private key into the private key file then we're gonna basically connect to the digital ocean so after we connect to the digital ocean you can see this single code so Wireless in this single quotes will now be run on our digital ocean file a digital ocean machine so first thing here is we got CD into the project right so make sure that whatever you have here matches whatever you have in make sure whatever you have here matches whatever you have in the digital ocean so uh you have to type PWD stands for uh present working directory copy this slash root slash Learning Journey Dash YouTube then you have a copy here so we're basically running uh whatever this codes is within the machine so we have to make sure that everything matches up so we got first CD into this uh directory we're gonna get reset hard we're gonna get checkout Main and we're gonna put in the latest change from the repo so make sure remember everything here is running within the digital ocean so basically giving we're telling GitHub step by step what the digital ocean should run to basically get uh get the code to be updated changes then we're gonna do Docker compost down which will basically shut down our current container then we'll remove all the other images and containers and lastly we'll do Docker compose up Dash build Dash D so if we run this command before so basically all this code is doing is we're gonna SSH into our digital ocean we're gonna put the latest changes then we're gonna restart the docker compose and then run the docker compose with the most updated code change so this is how we actually do the automated cicd pipeline so now what we can do is we can push this GitHub action up to uh GitHub right so I want to notice I'm going to show you something cool so go back to your GitHub come on come on to your GitHub repo github.com YouTube so come down to your repository that you have pushed right now we can see now there's this orange button right look at this orange button deployed to digital ocean which is exactly what we name within our GitHub action deployed to digital ocean so we can see that now it has failed all right and we can view why he has failed right it's because we don't have the environment variables yet right we can see that uh it cannot be it cannot connect so how do you actually put in the tree and rainbow variables here so come down to your settings page come down to your environments and we can see this production this production is exactly why we put here this environment production because any environmental secrets that we pull here must match this production name here so just press into production and then uh you have to add a new Secret so in this case the first secret we have to add is this uh SSH private key right so we'll copy this PC here wait so how do we actually get this SSH private key remember we need to uh basically come down to our digital ocean CD into the main file cd.ssh right we need this ID RSA so we do cats ID underscore RSA so now we got this private key I'm going to copy the whole private key copy in here and copy it into this and remember environmental Secret now we'll add a secret and the next thing we need is the SSH host so let me add a new a secret called SSH ssh host the host is going to be the IP address right so I'm going to copy the public IP address here of the digital ocean I'm going to come and copy in here make sure there's no trailing spaces add the secret and then the last one is the SSH the username so the username is just going to be root of course remember we are just uh Associated into SSH Roots at the public address right so now that we have all this I'm gonna come back here to my code right this has filled this action has failed right so I'm gonna try to do the automatic deployment now so if you come back to The Learning Journey let's refresh we can see that now uh everything is working if you refresh the page everything's working so let's try signing in with Google right hopefully this will work okay so in this case looks like it is going to work okay so now we are logged in we can see that we're on the domain and we can even see that we can create a course and we can see that we are on the manage a pro user we can manage the subscription and hopefully this will lead us to stripe okay so you can see that everything is working perfectly all the stripe integration is linked up so let's try the automatic CI CD pipeline so just to change it let's say I'm going to change this logo right I'm going to come to my vs code okay I'm gonna go into my nav bar and instead of Learning Journey I'm gonna press Learning Journey YouTube now save it and then I'll push it up to uh the logo change push it until GitHub so by right this will automatically deploy to our digital ocean so I'm gonna come back to my GitHub repo okay I'm gonna refresh the page and let's hope that this will work so you can see that it has detected a change and now it's going to run our GitHub action right so we can monitor it so right now it's gonna see that it's gonna try to run the actions that we have defined within the workflow is now failing so let us look at now why is wrong so one more thing you might need to do uh before making it work is you have to cut the idrsa.pop into the authorized keys right so basically what we're doing is we want to authorize our own SSH key to access it into our own server so that's why this command is doing so after this works right you can then come back to your or your vs code let's try again so I'm gonna just add one more space here so I'm gonna push it uh GitHub action commit and push it so hopefully this time it will work so if you come back to our rainbow we can now see that uh let's refresh it you can see that the GitHub action commit is found right and we can see that uh the orange button here shows so it's gonna be trying to deploy our website so you then run the jobs that we have described within the deploy the yaml and hopefully it will work okay so now it looks it looks like it's working that's good so we have basically accesses in we are then running the commands so we did pull the latest changes here so we can see that it has pulled down the latest changes from the nav bar then here you can see that it's just running the docker file so it's exactly what we did manually right so instead of manually coming in here and then doing Docker compost up let's just build it runs it automatically by the GitHub action then we can see that it changes the Prisma client and then it's trying to create optimize production build so basically what we did was we basically triggered the docker compost file to run every time it detects a code push so we basically automated the entire process whenever someone pushes the code up to the repo all right so we can see that it it finally works so everything works perfectly and basically now if you go back to our a website and if you refresh the page uh hopefully you can see the changes being updated so let's wait for a while and we can now see that the YouTube is shown here so we have basically automated the whole process right so let me just come back here so why is it important for me to show you how to do these GitHub actions cicd right because nowadays we take a lot of tools that we use for granted we take uh the Versa for granted right we think that okay we can just click one button deploy to verse out and you know everything is easy right Versa handles all the GitHub CI CD for you right so you don't ever ever get to see the manual process of setting up a proper CI CD pipeline so you should never get to really learn uh the ins and outs of how to link the IP address to the nginx to the different ports and how to use Docker so in this tutorial actually I've actually taught a lot I've taught you next year's I've taught you how to use AI open AI I also taught you how to use Docker after you have to use nginx I've tutorial how to use um GitHub action CI CD so there's really a lot to learn and I'm really proud of you for sticking all the way through and learning all about SSH and stuff so just to recap today I taught you how to do next auth front end back-end how to use stripe I've taught you how to deploy to a VPS I've even taught you how to use GitHub actions and finally link up the domain name with SSL cert so that's really a lot to learn and as always um I think I'm very happy that we have completed this project together and with that we have come to the end of the project so we've built this entire thing from scratch deployed it custom cicd pipeline so so if you enjoyed the project and you've learned something um please do like comment subscribe I put in a lot of effort into making this work and a lot of hours uh to filming this and making sure the project works well and making sure whatever I teach is accurate and correct so with that thank you for watching and have a good day thank you [Music] [Music] [Music]
Info
Channel: Elliott Chong
Views: 46,077
Rating: undefined out of 5
Keywords:
Id: EGW2HS2tqAQ
Channel Id: undefined
Length: 360min 2sec (21602 seconds)
Published: Sun Aug 20 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.