hello in today's video we are building an application which given the prompt creates a form using AI a form that can be published shared and filled out by others will create an admin dashboard where users can track their forms create more of them and see the results to their questions inside the dashboard there will be a settings page where users can view and manage their subscription if there let's say on a free plan they can create up to three forms before having to upgrade to premium version and at the end we will deploy our application and add analytics to track the usage and custom events we'll interact with our database using drizzle orm here's a SN pick of tables and here are the types of questions we have inside our forms not only text but you can see there's also radio and select as well there's a lot of learnings ahead like implementing authentication payments client and server components layout in nextjs and we'll even cover some of the new react hooks like use form state first functionality we are going to implement a feature that generates form using AI we'll have a popup which contains text area where users can describe what kind of form survey they're looking for Their audience and other details so we collect this information once they click submit uh we kick in this generate form action that will send a request to open AI API uh to generate the form content so and then the API sends back a response that contains the details of the form uh so that's what we'll do first and then the later parts of the video we are going to uh parse this content and turn it into an actual form uh that user can publish um and share okay first things first let's set up our nextjs project and we can do that by opening the terminal and running the command MPX create next app and I used at latest which basically takes the latest version of the nextjs and at the time when I'm filming this video the latest version is 14 so I recommend you also follow that version because uh just to make sure that all the code we'll write in this tutorial is compatible and you encounter no issues like this x module doesn't exist or something like that and once um it installs the pack packages required we say yes we named the project so I'm going to go with AI form Builder tutorial but the name is completely up to uh use so the next step is it's going to ask some configuration questions uh and we are using typescript in this tutorial also going to select yes for yes lint yes lint is basically defines a set of rules and good patterns in which if we don't follow thiss code will highlight for us and tell us um so it's good basically for catching errors early we're using ta Tailwind CSS Source directory as well and app router new for customizing import a is now it's going to install the dependencies once the dependencies are installed let's navigate to our project which is uh CD and the name of the project we'll install this components Library called Shad CN which provides a collection of components it's built using red and Tailwind CSS and they're super easy to customize you can also pick which components you want to have in your project so initially it won't install like a super large module like other libraries do and this is the command to run inside our project and time for configuration on this one yes for typescript I'm going to going to go with the default style and for the base theme from this option let's do slate for location for Global CSS file we're using Source folder so we have to indicate the path it would be a little bit different than the default and that would be global. CSS would you like to use CSS variable yes and we are not using any prefixes Tailwind config JS is located in under root folder and components alas so it would be this components from this Library would be saved in this um folder and import it using this command that we see here same for utils let's save it in the utils folder and react Ser components we will be using them now if we say yes it will write save this settings that we created in components Json file in our project pick a component and add it we're going to go with button so usually the way it works is you there have the options of components that are available and you select them and this is the command and then add followed by the name of the component and it's going to install the required code for that one more step before we jump into coding and this is an optional one and you can follow if you want to customize the theme and add some color rather than doing it manually through variables so let's go over first to UI cn.com themes and if we click customize here we have options we can select I'm going to go with Violet and leave all the other options as it is once you made your selection copy the code so it's going to show up here and we're going to paste that in the code I have our project open in vs code but you can use ID of your choice and inside it Source folder and then app folder is where we have global. CSS file this is where the CSS is defined that applies to any component we create throughout the application and at the top we have some Tailwind Imports we are going to select this layer base the first uh definition and we're going to replace it with the code that we just copy it so that is it let's save it and we can start the application now since we are inside RIS code we're going to use their terminal and we run mpm run Dev this will start the application on Local Host 3000 or if this port is busy then it will use another Port we can copy the link and paste it into our brow browser so we see this default default code that comes with the setup of initial creation of the project and it's inside page that TSX we can go ahead and remove all this extra code just leave the main container at the top let's go ahead import the button that would be from components UI folder and name of the component which is button and in here we add create for save that and we have the button in the them color that we peaked so this is a quick uh look at the folder structure so we don't have much this is the components Json file which is the configurations that we picked for Shad CN uh library and you can take a look make sure that it matches uh my selections then we have Tailwind configuration file here if we most of this are defined from Shen and uh if there's like any configuration changes we want to change that would go here we have types screen configuration it defines what kind of modules we're following and other all right we already set up a project and made our first change now let's create this popup that takes in user prompt for the form for that we'll need few UI components like dialogue label text area sh already has them so let's install it now let's examine how we can add a dialogue the UI library that we're using does have a dialogue so this is the command mpm that I'm going to copy open a new tab in inside terminal paste that and in addition to dialogue I need few other components from the library let me make the code bigger which I'm also going to add them this would be text area input label and the form so might as well in one command we can install all of this we can see here in UI uh folder that all these files were created for them um and here's the sample code that I'm going to copy and then modify based on what we are looking for so first we have the import statements at the top and then uh the dialogue tag and all its elements inside so let's create a new component inside the app folder I'm going to name this form generator since it generates the form and oops I'm trying to make it folder um and then inside there let's create a new file index. TSX and once we have more components for this form generator then we're just going to keep adding in this folder let's create a new functional component rename this index instead would be form generator with a capital f and here I'll paste at the top of the dialogue related Imports that we need and and inside the form generator I'm going to update this sample code with the one coming from the dialogue let's save this and before I forget let's import this form generator into our page so we can see the changes real time so at the top I'm going to import they in the same folder and instead of this button that I created I'm going to replace that with form generator to see the changes real time and now let's go take a look how looks like so we have this open button that opens up this dialogue with a text in it and we can close up by clicking X and we can go back to the form generator component and take a look at the code so we have it in dialogue um wrapper around inside we have a trigger that's a button that triggers the dialog and then we have a content with the header and the description one more thing I want to add is footer where the buttons would be looking at for like submitting the form so let's do that it's called dialog footer and we can do it below the header I believe it has to be inside the content this GitHub uh compile is kind of distracting so I might remove it so let's add the button variant I'm going to do link and this would be for create manually and I have not imported the button so let's do that at the top and I'm going to save this so if we navigate here open this we're going to see so the link if you usually hover over it just underlines U this variant but it acts like a regular button now this automatic triggers are happening but we want to control when it's opening when it's closing because when somebody submits the form we want the model to close for that we need to tr track the state and to be you able to use use T we need to make this component use client that's why at the top of the file we add this use client keyword so now let's create the state we're going to name State open and by default it's going to be set to false because the model or dialogue would be closed after that we can create a function that is going to open a model and we're going to pass that to the button I'm going to name it on form create it doesn't take in any argument and it just takes in um and sets open to true all right let's copy uh that function name and we'll remove dialog trigger since it's an automatic trigger instead we'll create a button uh and but to the on click we're going to pass this function that we just created and let's label this create form I'm also going to modify the styling for dialog content and for any of the UI uh components for shuten we can add tailing classes so small I'm just going to add maximum width seted to 425 pixels and I'll update this text for the title instead change it to create new form for the description I will remove this description completely and now after doing that we are ready to create the form and inside form we're going to have two elements text area and the submit button so let's wrap them in a div class that's going to be of um display grid and add a gap of four in between those grid items and some heading um on y AIS text area make sure that at the top it is important we're going to give this text area and ID U it would be prompt actually I think description would be um better descript better ID also name we're going to keep that as a description as well let's make it required and a place Turtle I'm just going to paste in the text that I have for placeholder and we have to tell dialogue which when is is it open when is it closed and we can do that by props that it's taking it's also called open so we're going to pass our open state to it and we also need on open change so because on some elements for example on the close close button it needs to update the state itself so let's pass set open to it and now if we look at the changes we see the text area we see the placeholder and if I start typing in uh the value would get updated and placeholder disappears let's close this now the time has come for our open AI integration open a has an API and several end points the one we're going to be using is this create chat completion we have to make a post request to it and let's change to noj see what it looks like it does import they have their own module but I don't think we would only need the one API end point I don't think it's worth installing the module so we're just going to make calls to the API and the messages and model are the required parameters so just like you talk to a chat GPT you can also make the same request to API and get the results back and let's examine what results look like it comes with the choices array and content is what we'll be needing we'll be taking that and parsing so first to use the API we need to create an API key so if you go to platform. open.com the left you're going to select API keys and you can create a new one give it a name um of the project so that you remember once you browse that and once you click the create secret key it will print out on the screen the value for it which we need to copy that in our project so where we store sensitive information environment variables is inside. EnV file so I'm going to go ahead and create that file file at the root of the project so new file envy and now here we can create a variable and I'm going to name it open AI API key and I'll paste my value in here once you save that don't forget to navigate to git ignore which is also in the root of your uh project and add thatv file here so what it does is basically if you're pushing to git you will ignore this specific file since it's sensitive information and you don't want your API keys to end up on GitHub so you stay safe from others using your open AI account now we'll create a function that will call the open AI API a function that runs on server in nexs world is called server action and that's why I'm going to create a separate folder for all the actions and here I'm going to to create a file I'll name my action generate form and the reason we're making this call on the server is because we're interacting with the API and to mark an action as a server one we at the top of the file use the keywords use server and ser actions can be important client or server component we're also going to import revalidate path from next cach and uh Z from Zod module schem of validation and when we install chats in UI it usually comes with Z but if you don't have it you can install using mpm install now let's create a function it's going to be a regular async function uh and we'll give it a name same as the file name is what I usually do and let's decide what our arguments are going to be and one thing to consider before we do that is that this action is going to be a form action we're going to call it when user submits a form which we have one fill currently the text area where they input The Prompt right so because this is a form action it can take in the form data and also the previous state um so we can Define previous state we can only just give it a message it would be of type string and we can also as I mentioned taking form data which would be of type form data um that type is a built-in uh for HTML input elements um and we don't have to Define that U anymore um so once we have the types now we can create a schema schema defines using uh Z from Zod module what kind of object we're looking for uh we only have one field in the form as I mentioned which is description so we create the object using Z that object we give it the field and we Define what kind what type this field is if there's any requirements for example maximum number of elements let's just do to minimum one so it should contain at least one character uh now that we have the schema we can access the description field within um the form data that was passed on and we can check against the schema if it matches so that would be what safe fars is doing so description if it's form data that get the description we get the field from for form data by matching the name of the field um so if it's not successful we're going to console log with the errors and we're going to return a message that says failed to string parse data so that we know what's going on on the UI client side Okay then if the the parsing is successful so we take the data from pars which could be accessed by pars that data and we try to make a API call afterwards so let's write a TR C patch blog inside here we create a constant response and using fetch which we pass API URL of the openi so I'm going to paste that in here and as a like a second argument it would be object that defines um headers which would where we indicate content type application Jon Json and also authorization here we have to indicate our open AI API key and we access environment variables using process. B but before that it's a good PR practice to check also if we have not forgotten to set the um open a API key if that's the case then we return a message um that the key was not found okay so now we have that and not to get the typescript eror I'm going to check if it exists and the method as I mentioned from the documentation would be post now as a body we stringify the data we have but also model is required so for this one I'm picking GPT 3.5 turbo and the format that they're taking is messages so for we're going to create an area of messages so for first one it would be role of the system and as a Content we paste in what user has described but we can also add additional uh description so that we get the response and reply in the format that we want let's add add the catch um to this um API call and return a message that if it doesn't succeed fail to create form and once we get the result we have to um take the Json of it and finally we're going to revalidate path and return the result which would be in format message success and data would be we're going to pass this J now to this data description that's coming from the user let's add our description of what we're looking for in the response so I'm going to create a new variable prompt explanation and I'm going to paste the text that I've tried and tested many times before uh so we're asking it the API call to return uh response in a Json format and we're defining exactly what Fields we're looking for what types of fields should should they be uh if it's certain types then it should come with like options for example if it's a multiple you can select mult multiple options what those options are um and what kind of questions we have overall um this and then what you see on the screen that would be the type that's coming back so I'll take what user described and then after that I'll describe in what format the form comes back so then we can take it use the objects and create um UI components and later and save it into our database so let's create using um template literals first data description than the prompt explanation now the function is ready or server action and you can import it into our form okay I'm just going to close the extra tabs and let's go back to the form generator and add the import statement below our other Imports uh think to note that uh oh I think I used the not the default one so every time I want to import con action I don't want to count how many files I have to go back and come back so i' would rather use this import alas like at actions and then the action name that would be way easier but because actions is in the app folder it's not configured and we can configure it by going to TS config Json where we see this path and then add slash so for now anything from Souls folder is accessed by ad but we can also add app folder inside Source folder everything from there to be also accessed in that way so if we save that now it's not giving us error anymore which is great and next thing we want to do is we want to import the hooks from react which would be use form State um and use form status this will help us get the form information from the form and pass it to our action um so first we're going to work with use form status the only thing with use form status is okay use form status Returns the status of the form when it's submitted so if it's pending U if it's been successfully comp completed or not and we'll create a new component for submit B button which needs to access this pending state so that we know when to disable it if it's in progress already and this use form status has to be a component child component of where use form state is defined that's why we created a separate component for submit button so if the state is pending we're going to say generating like update the text and if not we're going to keep the text of the button um generate same thing for disable is going to be disabled if um status is pending um and if not we just keep the type as submit and we let users submit their form okay so let me close at the closing bracket and in form generator which would be the parent component here we can add use this use form state so we have two parts State and form action uh and Define use form uh State first we're going to pass an action that's going to happen when user submits form in our case the generate form and second is initial state which we have yet to Define this is just before the form is even touched and updated by the user the state of um what's being passed to the action um so yeah let's define that form uh we can do it somewhere above our component so I'm just going to copy the name of this one and over here create a new state we have to also like um Define the type so we'll have a message um string that would be a required field and then data that would be an optional field and the by default the message would be empty because we don't have either error or success it's just no message um okay so going back to our form gener we can pass it to its prop as an action form action so use form state knows which form it's working with and when action on submit generate form is called the data that's coming back from generate form that would be stored in the state and and it would get updated um and we have yet to add a submit button which I'm going to do somewhere in the pholder right next to where we have a create manually button option and now let's watch the changes using user hook for the state which is once again the data coming back from when the form is submitted so in user fact um if like the message is successful then we can take an action to begin with like just close the model out because we have the data so let's do if state. message equals to success set open to false and we also have to when defining use effect pass it dependency in this case would be State message so we want this use effect trigger every time the message changes um so yeah pass that in the array and let's also console log um the state see what it's looking like and what other fields inside the data we have which is coming from opena and one thing I have to update is submit button I didn't realize that it's not part of the form so it won't uh be triggering the submit action for the form so let's make sure we put form after the submit button form closing tag and now we're ready to test it out I'm going to keep the console open so you guys can see uh the updates as we're getting so once I click create form I'm going to describe the form that I'm looking for um let's do something like form for developers so once we click generate we see the button is updating it's making an API call and we have the data back uh if we can examing here we have the ID and this is the choices that we're looking for mainly that contain contains the information in the form of messages and we have this is the Json if I double click on it uh I can see it in the Json format so o yep here we have so we have a questions array and then every element has a text which is text of the question then a type what type of question it is and some of them even have the field options that users can select now let's add authentication we're going to use next o for that and after we'll update so that only authenticated users are able to generate the forms and before we add authorized access to our creating forms functionality I will explain uh how the authentication will work uh we're going to have sign in sign up functionality for users as a provider we are going to use Google but the way I'll sign up you can configure to have any provider you can um connect it to maybe Apple ID U email send an email link or um other list of options and I'll show you guys how to browse them and we'll also afterwards create a database with a users table we're going to have more Fields than that and when we create an adapter that would connect um so when user would sign up you would create a record and you will also save at additional information about users so we keep track of our users and we know who they are use this mpm command to install the next o module after it's installed we'll navigate to Source folder and create a file named o. TS so inside the file we export next o function and we add the configuration for a provider which dictates how users can log in whether it's an email or using social accounts using this case I'm configuring Google provider so I'll update the import instead to Google and next up I'll update providers array for the nexos function Google provider does take in an object as an argument that has two Fields client ID and client secret the values for these fields should be stored as an environment variables as it is sensitive information so let's pass that and uh to The Experts let's also add sign in and sign up functions more on this later to add environment variables that would go inside your EnV file and paste the definitions or using variable names we used in this o file so we're going to need um only two values Google client ID and Google client secret and make sure the spelling has to match the one in o. TS and um as I mentioned there are other providers I'm going to I'm trying to show you guys the list of all the providers or like login methods that you can provide using next off but the website seems to be the search um overlay not working okay let me just select from the menu so these are all the other ways um that you your users can log in and if you click um single one of them it will show you the import method that you can use at the top and also field that you have to add inside providers array so you can configure using um this method any provider and afterwards all the once you configure them all the other code for authentication like functions will use for signing some now will be same so if I want to add another for example Apple provider I'm just going to add an import at the top and in the providers array I'll add a value like I'm doing now in this case and let me just this was just for reference I'm going to clear that out so we have defined environment variables in our file now let's get the values for them and I'm navigated for console. cloud. gooogle where I'll create those um credentials so click on new project and give it a name then we're going to click create and once it it's first loading so make sure you have that new project selected just in case you have other uh projects and we have to wait until it finishes creating so now it's done I'm going to select it I'm going to navigate from the left uh menu left sidebar to credentials and here create credentials um we'll select o client ID and then sometimes it asks us to configure a consent screen if it needs you to then this popup will appear click on that and for user type type let's select external which means it's AA anyone with Google account can log in and um here it will ask you information like the name and the email of developer and it'll show up on the right side when there's a login page for Google you know when you ask it for permission and which account to log in so I'm going to fill that out um save and then on the second script um for scope I'm not going to change anything save and continue so once we have this um all consent screen we can click back to the dashboard and on the dashboard um we can select the credentials one more time create credentials of client ID here we select application type which is web application and you can modify the name if you want and this is important part so make sure you don't miss it that in authorized redirect URLs you have to pass in the local host and then The Slap call back Google and I think I have an extra space let me remove that and create and this Ural would be different once you deploy it um so let's copy the client and the secret ID that it gives us on the screen now I'm ready to go back to the project and I'll paste this values inside that EnV file and one more variable that we need is this o secret and even in the old documentations this was not part of the setup documentation for some reason they only added and if you set it up Nexus without the secret it actually throws an error so it is in errors like debugging part where you so you generate it add that variable to your environment variables in the old version we had to also configure it to pass it to the next o right now the module would if you have it environment variables and if you name it or secret it will the module would automatically read from your file there's two ways of generating you can do it online or you can copy this Command right in your terminal which is what I'm going to do and you will return a value for me which I'm going to copy and then take it to my environment variables create a new secret with the name of the secret and paste that in all right now we need to create an API route which our functions would uh later use uh to call that API and that would be inside app folder we should name it API for any API routes that's the nextjs way and here will I'll create a new folder for o and another folder which is next o in Array this is how nexo module the route that also would read so you should follow this like very same names and to create the a route we use route. oras and in here I'm going to import uh the methods get and post from the O file that we have create variable names are incorrect I don't have get and post exported from o it's just a Handler so we can go back to the O file and um in handlers we can do a destructuring to just extract get and post specifically now if we save that go back to our R route file and um update the import path then now it should be working so that would be it for our API route and to test that very API route we can navigate to the browser and after the Local Host URL we can type API route off and sign in that should redirect us to signin page with Google we click on that select your account that you want to uh sign in with and it should redirect back to the index page okay now that we have API route let's put our authentication functions and information into our components so in my components folder I'll create a new file for header and create a functional component and there's two types of component server and client and we'll go over how to use o nexo in both of them first one this is um server component so we import o sign in and sign out functions for Server components this directly comes from the o f file that we created I'm also going to import a button and after the button let's do also import image because I want to display the Google profile photo in the header of the user so that would be image from next image and we also need a link for when we will create um butn for signning okay since we have that first thing let's access the um session information create new constant session and it would be a wait and this o function but since we have a weit we have to make this uh function async so let's do that at the top and we can take a pick at the session by console logging it um so let's do now we ready to update what we have inside the header return statement I'm going to update this div turn it into a header and inside header I'll add a nav tag where first we're going to have the name of the application or your logo on the left side and then buttons on the right side so creating a new div I'll just put the text inside H1 as a um logo and then I'll create another div and here this div would be conditional and we need to access sess session information because we're displaying different things if user is logged in right so we do session user that means that user exists in that case we create the div and if the user is not logged in then we display a link to the login page so API off sign in and we close this uh link tag and inside here let's add a button that says sign in so and then if user is already signed in let's just display their name so session the username once we save that let's import into our main page uh in page. TSX so I'm going to import the header and I think it's not it's an export default so let's update that and uh above the main section I'm going to add header and wrap this whole thing in a fragment if we go back to our application we're going to see the text that we added and signning Button as um that signed into the application now let's go back to our header file and add a sign out fun uh button uh for that that we need to create a separate component uh and I'm going to do that inside the same file since it's a pretty small component so function sign out and it will return a form with a button and inside the form we're going to pass in an action I'll Define action right here as well since it would be a server action so it's async component and the server actions we Define we mark them as us server keyword and after call await and sign out uh from the next O So once I have that inside the form I'm adding a button and type would be submit so when it's clicked this form action would be triggered and sign out would happen so now I'm going to remove um inside the div instead of showing the name I'm just going to show sign out button so let's save that and add some styling for the header it would be some border that will only show up at the bottom and for the nav let's do class name background white also up dat the Border color add some paddings on X and Y AIS okay that looks better and I'm going to make the div inside Flex uh Flex rep and justify between with centering items and a margin setting it to Auto so it the items display spread apart um like that okay let's also update the variant of the sign in make it um link so that'll change uh The Styling next upep I want to display the profile image of the Google account that user has but since the URL of the image we do have that as a part of session session information but to display a image from another website we need to configure in nextjs so going to next. config.js uh follow the updates as images then remote patterns so for protocol https then for host name we're going to pass the host name that Google uses like the URL of it Port would be entry string so the path name would be this a and stars um and then this I followed after examining the image URL so once we have that configured now we can create an image and pass that um user image as a source so I'll make it right next to the sign out button image source equals to session user. image we have to also add the alt text which would be just a user.name a width and height a required I'm going to do 32 for both of them and for class name I'll do rounded full as I'm using um Tailwind it would make this turn into a a circle and uh let's also check that usern name and user image is Avail available in that case we'll display uh the image and um oh the sign out I accidentally removed it so let me put it back okay I'm going to sign in right now with Google and now that I'm signing I see both of them let me also update the class name make this uh D flags and uh item Center so they show up side by side okay so that's what we have for our image and sign out as well as for the sign in buttons if we want to examine what other fields are part of session we did this console look so you can check that in the terminal so we're doing it in terminal not in the browser since this is a server component so console would show up here for that one and you can see the pattern of the image URL that we defined in our next config otherwise if we just try to call the image in the new version of next to throw an error now let's go inside the form generation which um so when user clicks here we we're going to check if they're logged in or not and inside index. TSX as you know this is client component and here if I wanted to get session information I'm going to import uh the use session hook instead from next off react also I'm going to do sign in function and um yeah so it will be next o/ react which um comes once we install the next o uh library and inside my component here uh at the start of the definition of component I'm going to create a new um session variable and I will call this use um session hook um so when there's changes made to the session this hook um gets updated and we can examine also the information about the session session I'm going to do another console log session this time it should show up um in the browser but before we test that if we want to use use session hook we need to make sure that component is wrapped inside um it leaves inside provider um so for example here I'm going to go back to the page and we have a header that server component we created and form generator is the client component so what I'm going to do is I can wrap my whole application inside this next off provider or I can only Peck the client component that I need to access this use session hook uh so let me import this session provider at the top and it's also coming from next o react so let's add that session provider outside of the component save that and now if I go back uh to my client component and save the changes in here here in the browser let's examine our console and we can expand on the object which is session object and we have this information coming in from console look and um I also imported at the top signin function let's say I want to restrict some functionalities to only signed in um users and I want to check that we can do that using if statement and we either check if user data that session data that user exist or if the status equals to authenticated so if that is true then I'm going to continue with the functionality open a model but if not then we can redirect them to the sign in page so I'm going to move this code over here and in L statement here I'm going to do sign out and yeah it's sign in um and yeah we imported that from uh next o uh react module and let's test it out I'm going to go back to the application and I'm going to sign out first so that when I try to do this on sign in it will redirect me to the signin page now that we have built out an authentication and the functionality to generate the forms we need a database to store this information first about the user and the forms that they generate and we also need a way to interact with the database as for the way to interact with the database we are going to use this typescript orm uh it's called trizel it's a very lightweight and it's known to have a good performance over other um orms and also you can deploy Edge functions on it and the database that we are going to connect is going to be super base so if you don't already have an account in the super base you can go ahead on the website and um create an account by the end of this chapter this is what our database is going to look like so we're going to have several tables to handle and save authentication information starting with the user then we have a session that comes with a token and then the time when it expires so when session expires user is automatically logged out from our uh website and also a account that also has a relation to session and user and finally verification token which is coming from the provider in our case um Google I'll navigate to your dashboard of uh inside super base and we're going to create a new project give it a name I suggest um generating a password and then you can copy it and save it in your password manager and let's click create new project once it is done creating a new project on the left hand side we're going to navigate to project project settings inside here select database and where we see connections string URI we are going to go ahead and copy this string come back to our project I'm going to extend it and let's save it as our environment variables I'm going to name this database URL and that equals to so in here you have to change this instead with your password so I'm going to paste mine created a database now we have to do two things first we can connect our next o to drizzle that would require additional setup which comes in form of an adapter and second we have to connect the drizzle orm setup so that it takes request and it's interacting with our database so next those to drizzle drizzle to set a super base so on next page we have this um guide to how to set up an adapter what to install and also in what format uh we have to create our tables in and also I forgot to mention the database that we are creating is postgress database let's first get started by creating um download the install so while that is installing we can update the configuration of next us so I'm going to navigate to our route file actually this is um in the older setup in the new version we have this configuration saved in of that TS and uh in here first let's import drizzle adapter at the top and we have to import schema which we're going to create right after making changes to this one and pass this as well for this schema I'm actually going to create a new folder inside the source folder that would be for all database related uh files and create a new file let's name is schema. typescript in here we can copy this U setup that includes creating the three tables that I just showed on the visualizer so sessions verification tokens and users as for just in drizzle we create tables using importing PG table because it's postgress and then the types of when we're defining fields we indicate the name of the field and then type of the field and we can have also other setup for example like it it's like required type and it's used as a primary key um then after that um this is how also we for example example Define relations but we can get more into that once we create other tables for example for forums and questions so for now let's save that go back to alts update this import because we did it from the DB file and to be able to create this DB instance that we can pass it to adapter comes the second part that I mentioned where we have to connect drizzle to our superbase database URL string that we saved in our environment variables that's what going to help us so inside DB I'm going to create another file name it index.ts here first at the top we need to import and we are using postgress JS after that let's import post grass it seems like we have not installed the post so let's do that mpm install and finally import the schema which defines our tables and setup now we are creating a new variable connection string and that would equal to process. EnV database URL and we'll create a new object client and that equals to instance of a postgress which taking connection string as an argument and finally we are going to export constant database that equals to drizzle and as a first argument it's taking in this client and we're going to pass schema object as a second and as for connection string because it doesn't know for sure that this envirment variable is defined that's why we're getting an error but if we give it an alternative then the error should go away let's save this let's update and import this from DB index be like this going to save that we have an index file we have schema file so that creates a connection between drizzle and O and finally we need a drizzle configuration file which I'm going to create at the root of the project so new file F drizzle config TTS and the configuration would be following we're going to import type of configuration from drizzle kit and then we'll Define schema we'll pass it the path to the schema and where it's supposed to generate the output so you'll create a new folder drizzle so then definition which driver we are using which is PG and then the credentials here once again we can say if it doesn't exist let's do like a local string and then definition of type so we're using satisfies config let's save that and after making changes to the schema which was adding tables we went from having no tables to having I think U three or four of them but this changes if need to be pushed to the database and the way we do that is by the push command from trizel but when I run this so the command is MPX dzal kit push PG I get this error that um transforming Conant to configure Target environment es5 is not supported yet so it looks most likely an issue from um the way we're defining so es5 environment not being supported so what I did to resolve this in issue is that in TS config file here Target we have es5 since it's not supported I change it to 2017 then if we save this close this out and try again it says changes applied now how can we confirm that changes have indeed applied we can always go to database instead inside of super base and inside table editor now we're going to see for tables but of course their data would be row but browsing data in super base is pretty simple we want once I authenticate right now the they would be information would be here added as the rows and the columns are the fields of the table now let's test out the connection between nexos and drizzle so if I say sign in here it should create a new of row in our account table here and you can see it indicates the provider Google created a a new user with filling in this Fields so yeah that way we can track our users but as I mentioned we also need a way to track the forms they have created so when they log in they can see their forms modify they can see the results so for that we're going to create uh as the next steps forms table starting with the forms we are going to create two more tables one would be the question and another one would hold the options information for example let's say if the question is um do you have any experience with python the option could be yes or no um so for the forms uh first we're going to add an ID so serial ID and this would be the primary key so our unique identifier next we can give it a name going to do to name description of type text and um user ID for this we are going to create a relation but for relations we create a separate variable which um I'm going to do after I'm done with creating the tables and serial is red because we need to import that from drizzle or package okay I'm going to add one more field here so that would would be if it's published or not user would have the option to select true or forceful this and make sure to also add Boolean at the top of your Imports next table export constant questions that equals to PG table questions um ID would also be type of Serial then we have the text of the question so this field will determine what kind of question it is specifically what kind of answer it takes is it a checkbox multiple select radio or other options and we can Define the options using so it would only allow certain values for this so export constant I'll name it form elements and that equals to also using dzel PG Ino we give it a name I'll do this will be field type and let's pass the array of all the values that are available and also import this inum here so pasting all the values we have except for the checkbox radio select input text area or switch and in here we are going to take the name of this and pass it in the field type so it gives us the array as an option and then we need one more field form ID we need to know this would be of type integer and which form does this belong to and finally field options ID text also value and the question ID because this would be based on the question so text would be what would be displayed on the form and value would be what value it's given so for example if there's any calculations that's needed on form it could say one but the value could actually be the number one okay now we need to create the relations between this table starting from the forms it would have a relation to the questions so for this one we use relations object which we also have to import from drizzle and then we pass the first as a first argument the table that we want to Define relations on and then we pass the call back for the relations so first I will pass both many and one and for the relations we have to separately import that from drizzle orm and first relation we are going to have questions so this is many to one relation so one form can have many questions but question only has one form and then we have user which is one to many relations so form is created only by one user so we first pass the table that we're making connection to and then we Define how does this connection work so it would connect on for field and it would be forms. user ID because we have created this very variable here to store that and as a reference you would take in users. ID since that is what's unique if we take a look at the users table and it should be fi that's it for forms relations now let's move on to questions relations that's this in this case we also need one to many so first relation we're defining is with the form and question belongs to one form Fields would be questions. form ID and reference it would reference the ID on the form and the second relation is for fi options and we're going we might have many field options so for example yes no maybe to answer to a question and finally we need to indicate that field option belongs to question so let's say in result you have to Define relationship in both ways so if we just leave this like this it's not going to infer field options relations we have to also specifically Define that field option belongs to a question so let's do that let's see if it gives us uh the correct suggestion field options as the first argument second argument we have one and relation to the question we have one questions field would be field options. question ID and then reference would be ID on the question itself all right I think that looks good we can save this push our changes let's take a look at our table so we have questions forms and field options so if we go to database and schema visualizer we are going to have something like this oops so there's forms there there's questions and then there's filled options and if we go to the table let's say forms table we are going to see that we can insert a row here for example name form number one description published false and I'm going to save this now let's fetch this data from our application side and we're also going to learn how to write a simple query just to fetch all for our our forms table so I'm going to go back to our and let's just display the forms on our index page so going to navigate to our page file and in here we can make home and async function and we need to import DB to run the query from and we have to also pass the schema or just the forms table from the schema a sync and we're going to add await constant forms equals await dbquery do name of the table and weall find menu on it first let's just console log forms make forms make sure they're coming in so it should be inside your terminal console I'm going to save this just going to reload the page and instead we do get this one element in our array let's create a new component to display the forms and for that we can utilize a card from and let's install the code for this and I'm going to create a new folder for all the forms related stuff of forms and the new component file here let's name it forms list so the props here would be we're going to take a list of forms but we need the type for the fors and the type we can infer from drizzle package so let's create type form and that equals to infer select model type of forms and we need to import first the forms from rdb and and the infer select model comes from um drizzle orm right now we have created this type we can Define this um indicate that forms is an array of the type form and we'll import what we need for the card from Shin which has already been added as you can see here after I ran the install command and it comes with card content description footer header and title I also import a button and a link which would go as like a footer of the card and okay let's take now the forms that we have and display them in the green so that would be forms. map and we're going to take form that would be of type form and return it inside of the card and it's props do forms not just forms so we're going to map that out but also let's now let's define uh start adding fields to the card so for key because for my D is unique we can use that as a key here next we're going to have card header aside we can use the name of the form as a card title so that would be form that name and the description after description so I'm going to make this bigger so you can can see better finally we have footer and as I mention I'm going to add that button in here let's first create a link class name I will do with full and H would be forms ID actually we're going to create the path for this maybe it will be like add something like forms edit and then form ID let's close out the link and add a button that says view as for the class name I'm going to do this one as with full as well so I think that should be it now if we go back to I'm going to go back to the F page import forms I think it's export default forms list and below the forms generation we can pass the forms that we fetched to the forms list let's save that see if we have oh and then you can see the card displayed here and I'll add a grid on it so in case we have more cards we are going to display this in a grid so it looks better grid let's define columns one and on medium so on larger screens we're going to have three columns some margin some padding and a gap and for this one I'm going to Define Max with just in case the name is too long of a form so it doesn't go over let's save that I believe I have justify between in here that's why it's displaying it below but I'm going to remove that moving on to the functionality where when we click create form pass in the prom and the form is created from the data is coming from open AI let's save that to our database for that we will need a server action to run the mutation on the database so I'm going to create a new file I'll do mutate form mark it as use server as for like the mutation we already took a look how to uh run a query find data using drizzle and now we're going to take a real use insert from drizzle to do so um okay import DB that will need forms table we are also going to create questions so I'm going to import them as DB questions and field options from schema and we also need off because as you remember inside our schema for the fors we have a user ID so the data would um be of type form except it's not going to have any IDs and we'll construct that form after writing the functionality so let's get user ID that would be session equals to A wait o all right how do we insert insert form that would be constant new form8 db. insert here we pass the table name that we want to make insertion in and then comes the values so values would be this all three that we have name description so questions is an array so we can't directly pass the questions uh we can pass the user ID and description and we can also indicate in this uh insert what do we want it to return that is using returning we can return everything but we don't need any everything we just need an ID so that's why I'm going to call it inserted ID and it would be forms that ID and then us um The Returning is also in the form of an array from our insert so it would be new form first element of the array because we're creating only one form and then inserted ID which is ID of that form instead that will be stored in here so the other steps is this one just creates a form but we have to also add attach the answers to the form and for each answer we need to attach the option before we add the logic in here so to do add questions and options let's test out this functionality and one more uh change is published by default would be false until user selects that they want to publish it so let's save this go to the generate form because this will directly happen once we get the data back from open AI so I'm going to import save form from mutate form and once we have so we're returning anything form ID await save [Music] form so as a questions we're going to pass the questions coming from opena but first we need to do some parsing so the format of what they're returning is choices arrays so we'll take the First Choice and then we'll take the message and the content of the message and as you recall this is coming back as an string so what we need to do is we can do json. pars so API response is always string convert into an object and we can console log TP form ID you can also console log the open a response just to double check that this format is correct I'm going to save this I'm going to have okay as you can see the form got created it has the name testing save form and description so that was successful and if we go to the terminal first we see the return response from and open Ai and then we see our form ID which is two and the new object created in our database um table so this is fetching it back now that we are saving of forms creating new form in the database and saving some meta data like name and description we're also passing the questions but questions are not getting created yet and we can add that as we mentioned in the save form function uh inside our actions folder and first we are going to to prepare and create an array of objects that would be added to our database so that would I'll name them new questions and that equals to data the questions that is coming in as an argument and let's map them out in here I'm going [Music] to return test um text as the question has also because we have return statement this would not uh run unless we move this return statement anyway so text of the question we want question text field type would be question field type as for the field options field that would also come from question information but one thing that we would add to this question coming in as an argument would be form ID because we just created the form so we just found out what the ID is and I think that should be it for the new questions now since we are adding multiple items into the database so kind of like a bulk update it's best for us to use and more efficient a transaction to do transactions in drizzle we take away we use away DB and then transactions like this and then we pass in a function that we want to run so in this case is an async function we're going to go through all the questions so for constant question of new so let's take the first um item of the return and it's question ID let's do A8 then transaction and here as we did before we're going to do an insert statement so into the database that uh the table name that we imported from the the schema we are going to add a value of question that we're Ming out and then we determine what is uh returning and for that we're taking name the field question ID and it would be the field ID from D questions table all right and then we can check if also the question has a fied option then if they do we have to create a new item in our database for that as well so if question that fi options so if the length of the array is more than zero then we do another insert so this time it would be in field options table and we can also inside the values here pass an array it's totally fine so the array would be question fi options and we're going to map out um every field option for the text it's going to take in option the text value and then the question ID we need the ID coming from database of the question that we just created so the relationship is mapped correctly and we can leave return statement as uh for my did that is totally fine let's save this and one thing we have to update is that the data questions is coming back as an object that contains filled questions so I we can either do this or maybe we can take I guess this is the better option let's remove this from here go inside generate form where we are calling save form and when this is coming back we can access the field questions instead so I'm going to save this save this changes and let's generate a form and then for examining the and for examining data we don't always have to go to super base we can also use Studio from drizzle I think that would be a better option and looks like I don't have it requires us to install PG let's see all right let's do that first now if we try to run it it's MPX dzel kit studio and it should give us the URL where it's running let's open it and examine our data so if we go to the questions we can see that two questions are created and this was from the last form I made a form about u s C++ so what is your proficiency level and then which courses are you currently taking and it Maps out to fi options these are the options with the text and the values inside this is nice we have questions and the options for the answers generated by AI dynamically stored into our database and one more two more fields that based on the form that are not dynamic as you can see I have hardcoded here are the name and the description of the form so let's also make AI generate that and for that we just need to ask where the prompt uh we pass the prompt to also return return the fields name and description so that would be in the same file on the prompt explanation with questions array where every let's see three Fields name let's do string description also string and a questions array I'm going to save this and also for the response let's do save response here I am going to copy this oh we already declared response I guess response object and we can take this all right I'm going to save this and the name and the description was actually part of the question so let me modify and say survey let's specify that it's an object we try again now it actually uh follows what we want so it returns back the name uh generates a description and also the question so that's the the the one that it just generated speak application form and we can double check that the questions also are coming in so yeah here's the full name description and all the other questions belong to the latest form and one thing left for our generate function form is to Define the types of the data that we're passing in and I probably should have done this before finish in writing the function but let's follow the similar example as we did to for the query where we use this like infer model in case for uh when we're doing an insert we're going to use infer insert model so that that omits all the IDS that and primary keys that are generated on the drizzle side because we don't have to pass that and to insert um to let's first import that at the top so it would be import infer insert model from drizzle or Ora all right let's create types so form type would be type of forms then we are going to have a question and fill option type and we are going to add questions that is going to be an array and each it's going to have all the questions from the field in addition to that it's going to have another field that is field options that will come with this um field option type of an array and we can take this all right so now you can see that before we had red lines on the question also for the option now if we hover up because we have not defined types but since we know what type this data is and we have defined all the fields it will know for example what type of option is and question and so on so back to our form generator instead of just closing the model we can close the model but as well redirect the user so first we need to import the redirect from next navigation and do that here and that would be forms edit and we are returning a uh form ID as a results so I'm going to put form ID in data okay so once we have that coming in on our client side will be St data and form ID all right let's save this so This approach works it does redirect us to a page um the correct VL but because we hav't created a page it tells us that this page could to be found uh even though this works uh when I took a look a better look at the mentation it does say that we can use it in a client component but it has to be through a server action so I don't want this throwing any errors later on and I we might have to create inside the actions new action similar to this so I'm just going to copy that uh I'll name this new file navigate to form let's paste this and we are not going to get um data ID we're just going to get an ID that would be a number let's see if the ID is s serial it's probably going to be a number forms edit and just the it okay all right now we got redirected to this one let's create a new page and as you notice that for we would need to also have a dynamic path because this number keeps changing based on what the the IDS so first things first if we are creating a new page for the path for it to be forms added it should follow the app folder structure so what that means that if we created a page in sted forms then the URL would be uh Local Host 3000 the main base URL and then the forms so if you want to continue with the edit then like a sub path then we create a new folder name it edit as for the D field that I mentioned for that one we create another folder that has brackets around it form ID we give that variable that changes the name this case it would be form ID and finally for creating the page itself where this logic would live it would be page. TSX it's just like page is just like any other component throwing an error because I have not created a component but now when I do um export default page it has to be export default now we get this um page in here and it's actually the URL is this I just started writing that's why it doesn't show so because this is a new component also created inside app folder and I hav not used client by default this is a server component and now I'll show how in server components we can access the parameters specifically this ID that we're interested in uh first we are going to update this props and this will takeing instead params and we can Define what we're taking uh which is form ID and that is a string and to access the form ID we destructured it here so that would be form the equals par for and also if we it's not provided for some reason we end ended up in this page then we can um redirect to user show that form hasn't been found if not we can display the form maybe here and here it is now let's look up the form and get the questions from it so that would mean running a query against our database so first I'm going to import the database also import forms we also will need to make a conditional statement because we don't want to get all the forms so that's what equal comes in and finally with attribute for creating form and make this um component a sync I'm also going to delete this extra definition and let's do form equals to await dbquery forms find First and here comes the we conditional statement and we're going to use equal function where First We Take in the forms. ID and that should equal to our value for parameters but because in url next always con even if we pass the number it's going to read it as a string that's why we need to parse in and take this um form ID so this will just return the form but what we're interested in is these also the questions right um so for the query to return the all the related objects we have to specifically Define it with WID um so that would equal U questions and inside questions if we just want questions that we passing true and that's it but we also if we want another nested field then we have to create another object that has a withd field and that would be field option set to true and before we display play Let's console Blog the form and actually let's check first because if there's no form ID there's no point of running the query here if I reload this page I should get all right here it is so we have the questions name all the other fields if user is not the one who created this form then we don't even want them to see this edit page or uh able to edit it so here we will also check and that would be first we need to get the ID from um O So import and we can get the session information a wait off user ID would be session user ID and we'll change the statement and instead we would be you are not authorized to view this page one thing is we need to be checking if the types match so this is string and this is string as well okay should be good um um that that's weird going to console log form and user ID all right that's because we are not getting the ID from the database as a part of the session by default in next o so that's why for the forms all the user IDs are n and even in the session to change that we have to go back to of that TS configuration and add a call back and that would be in the callbacks field and we'll modify the session so when session takes in session and user we are going to say that modify session. user. ID and make it equal to user. ID and we'll return that session all right I'm missing it was not giving me this error previously if I reload page let's see if user is getting saved ID the ID is coming in okay so it does say that it is session user but there's other options could be token it could be new session might be undefined let's define types of session session would be and the user and make the user optional and in here we can import this types from next o it would be like this and we can say if [Music] user then update the session and the user and the session all right I'll save that and we still need to have the ID coming in we get redirected to the new form and I'm able to sit because I am doing author of the form close this extra text and now let's add to our page a layout so everything it's more like I we have a form centered and there's some space around it layout file goes inside the form folder so we have our form here if you want to have every route of edit have the same layout then that's what we would do and same for like form my this so I'm going to create I don't want the layout for forms but I'm going to create one inside let's let's do form ID and we have to name layout files layout this would be actually we don't need to create a component let's rename this form form edit layout and as for props it's going to be react uh children children of type react react note doesn't have implicitly type any oh because I have a comma and here we are going to display the children and let's change this into a main class we're going to make this Flex minimum screen minimum height would be screen Flex Direction column items centered justify between and the huge padding 24 let's see so here we are all right for constructing the form we are going to use form element from Shan and also it comes with other parts of the form for example label form fi and we can take a look at the example it is using react hook form and also sod um and this is the basic anatomy of the form where you have form tatp form field and then your pass controls name and you render an item that contains label and the control and control is whether it's an input checkbox radio or however answer is passed to that form and this is a basic example where we Define this use form hook as for installation this is the command that we're going to copy and then there's a part one of defining schema we did this uh for when we created a text area then we have have to Define like a submit function how we're handling and building your own form at the top once again is this form element coming from shety and then a basic form that has an on HTML form on submit function maybe some styling and we pass we're going to map out and pass our questions as form fils and finally we have this submit button all right let's go back to our project and and inside components I'm going to double check oh we do have form installed okay yes for the text area initially we installed that and um so we don't have to run that command again and I'll create a new component that's going to handle our survey form and or survey I guess form I will add that here form. TSX let's create a functional component and and what we're going to do is we're going to add this form not in this page but in this one as well um and we're going to pass it the form data that's coming back from our query but we need to define the type so for the form type we're going to use one once again infer select model but since we the types of form question and field option can be Reus throughout it's better to Define it inside a types file um so let's create a new folder inside our s Source folder I'll name it types and the new file form types. d. TS here let's first import infert model from drizzle and Define type form select model that equals to infer select model and type of form but we have to also import that's weird forms questions and filled options so we'll do type of forms not mode but model that would depend on type of questions and fill options and based on this we can create a type that would be a form that has questions and fill options actually let's import them first all right so it extends from select model and it also has a question that is a question select model but that also extends and comes with filled options all right and as a props we are going to take in form that is of type form here we can display before we create the form just the form name so we imported the form now let's our component and instead of just displaying the ID we are going to so undefined is not assignable to form so let's check if form exists of right form not found types of question questions are incompatible CU it should be inside here so okay now the error is gone and let's navate to our page reload and we see that we get the title back let's add some styling for the header text large let's do font bold and some paddings we can also add a description that would be props form description I would make this text medium and maybe we should Center all this what are we thinking yep I think that's better following the example of the form which we're from shuts in documentation which we're going to modify to our needs first we need to import uh form elements but while I'm doing the import I'm also going to change the names um using eliis because we also have same names like for example form so I'll import this as form component then next we have form field let's do sh CN form field form item we can keep this the same and from component UI form all right and then we also need this use form which is coming from react hook form so let's go ahead and move that and main thing to remember is because we are using react use use form we have to Define um this component where it's used it's a hook so it needs to be a client component let's set that use client keyword at the top and I think that's it besides let's do Button as well we're now going to need this for the submit button all right and then let's create the hook for the form so I'm going to add that starting the form definition that later on we're going to pass that to our form component So Below this this is just like form information here we can start creating the form by first using for component and we're going to pass all the fields of the form like how it is here let's close this component out and it's giving us ER because we have not added the field so we're going to add that after we also have to pass this onsubmit function and let's name that um handle submit we'll Define it here but for now we are going to console log it let's see what example is used here oh it's taking in the values so I'm going to rename this as data I'll leave it as any because we don't have the schema yet and let's just console log the data so we can see inside the form we're going to map out the questions and let's see what this is offering us form I so we're going to change actually all of this or even this let's just R this so Props that form the questions map and question would be type of question select model which we have to we have already imported and index would be number so starts with the zero goes up to however many items in this questions array we have and and then we will use chuts in form field as uh we have like form field here uh we have to pass control we're going to take that as form control as for the name we're going to give as question IDs because every question has an unique ID so that would give us an option to identify it each question I'm going to do lower and right right next key which should also be unique and this one I'll take in to question. text followed by index and finally we have a render which would um we pass it a field and it is going to render it so like this all right and then form I here we can have let's start with form label first that would be question text so that and is remembered we have different tyes of form questions and their type depends on what which one of these are they so these are all the options and be for all of different options we have to display different form control so for example in here we have form control and then inside here we have input but first we have to determine what type it is read that information and then conditionally display the type but for the labels it's just text so it would be same for all the questions and here we can see um all three questions or two questions I guess we can also give open AI indication to add more questions because that's very short amount and before we move on to some conditional logic let's add classes to our form I'm going to make this grid with would be full x with three EX cell items Center adding some Gap and the margin and we can update the four label as well let's do class name text space and margin at the top for the form I think the text on the left looks better we can also give it numbers so index + one do space something like that and let's add a form control here and inside the form control we're going to create a new component that's going to handle the conditional display of the controls so new file on fi let's create a new component so for this component we want to use a certain components that we have not installed so we're going to go ahead and do that first it's going to be select we also have radio group type so let's add that and a switch at the top we can import all the items that we'll be needing in here and as for the props first we are going to have be passing an element that would be of type um question select model and we can import that from our types so let's D structure this here and instead of having just a lot of switch statements like if El's I'm going to create a new object and object would signify the type of the field that we have uh in the form and the value would actually return that component so let's say if the type is in input we're going to return an input as like a react component so let's name this components and first we're going to start with let's just do input and return first we're not going to pass anything it would just be a simple input let's do same for switch and I think for select we have to Define more items let's try text area we have that imported yes so in the return statement we are going to return we're going to take the components by the field type so element type is what defines a field type field type and if it finds any matches then it would return that one and if not we're just going to return none null uh let's see what's so if element doesn't exist return null and so it's um underling because we haven't defined all the conditions here that are available in the field type but we're going to get to do that changes let's import the for form field in here and we're going to add that in form control and the element we're going to pass that to be a um question as for the key let's do index for that one right let's see if any of this types are input uh the type is field type all right the second question was input text so it's um showing the text to us now let's add also thep types for I think radio and select is what we're missing so this would be a little bit more I guess logic because we also need to take the field options and map them out so the first is going to be select trigger so outside we Define select and then inside we have a select trigger select an option that could be our placeholder and then comes the select content so we Tak in the element and oh we have to use the extended um question type because this one doesn't come with filled options that's fine we can import field options select model and this would be field options array it's going to have this array all right so we M that out we take in the option it knows what type option is and then the index and then we have select item and we also in addition to the key which I'm going to update because we have this index in other Maps so this one has to be like unique and that would be item. text and we're going to also add item. value to it so as I mentioned we have the text that's a display text but we also need to pass not item option it's required to pass a value and that would be option the value we already do store that string or n is not aable to string I guess the value is optional which it should not be think we're missing if we save that we don't have any select in this question so that's why it's not showing and yes let's add a radio group so we WRA that with radio group element fill options we map them out and as for key it's also option text option value um actually option value is not always defined so instead what I'm going to do is I'm going to use option. ID instead and the number is not assignable to string because when we store later on in the database we need to know which ID it is so we actually store the information and we we can do something like answer ID same approach with the select as well but in any case if we ever need to calculate let's say total values of all the questions we still have access to the value in the database um so we can use that instead all right item we also have to add for form control which I'm going to import first form control and form label from our form components and going back here this would be wrapped in for form control first and on mapping on the main I guess wrapper should always have the unique key so we're going to move key to this div here and we'll also add some styling Flex item Center space on the xaxis to so we have value let's define ID to be item value and we're going to make that to string it an option and I will import label and it would be followed by label class name let's do text space option text um then we'll make the ID if the value is empty ID would be same as the value all right so now we do see the radius and if we select one in radio groups you can only select one so on the screen we're able to see the values that we have selected but if you notice if we were to submit this form we would not get the data oh I guess it reloaded but um the data would not be available in console that's because we have not passed um on change and value functions and we can do that first from the form um so inside here it would be value equals to field. value and then we have on change and that would be for field that onchange and we can add those um in the types inside the prop so value and then unchange and we have to pass this to every component that we have which is a bit tedious but I'm not sure if there's a better way of doing this so let's go ahead and do that starting with the radio group in here is called on value change and we're going to pass on change which we can destructure here and default value actually we don't need to set a default value next up we have select we have once again on change for the input the prop is called onchange because this one is expending expecting change event handler Instead This input so we can update the type to be value and event change event handler and we'll import or change event we'll import that from react so let's do it's so the value is either string or change event okay let's save that let's save as passing the value and onchange on the form recap what we did so we took the form information and displayed it on a form page that is unique to every form and um so this form component is what handles displaying the form and its fields and we created the fields that matches the logic that we have for saving like different types of fields whether it's input switch text area or select radio group and at the end we display based on what field type is provided in the information that we pass and uh eventually what we're going to have is so this is the form that on user generates they're able to see but obviously they're not filling out this form they are just like looking at it it's their form and they want to publish it so make it available so other people fill out and also later on we're going to add an edit functionality where they can edit this fields or types of the questions um so for this form we're going to be used just to display and later on for users others that are not form owners to fill it out so we're going to have two modes one would be edit mode and one will just be like a default form mode if we can call it that so for that for users that are creating the mode we're going to have a publish here so let's uh change that so it would be the form itself that accepts another uh I guess argument uh another props that's like optional so I'm going to make that like this and if it's a Boolean so if this one is true then we're going to display publish and also in handle submit we're going to have a different um I guess action it's going to actually submit versus if it's just an user um submitting the form then we're going to save their answers all right so let's do here edit mode Let's see do we have this let's destructure it edit mode equals props and if edit mode is true we're going to display publish and if not it's just going to be submit all right and then we see no change yet because we need to pass through from our page which is an edit one in here I'm going to do edit mode equals to true all right now I see the publish so what publish is going to do is is going to take this form and Mark it uh published as true so if we take a look quickly at the forms table we have this published which is by default false and we're going to take the form ID find that form and make the publish true so uh let's do that and let's create a new action for it uh inside actions folder mate form we can have add another one here this would be export a in function publish form and it would only need form ID as a number and let's see so we need to do a wait DB and update pass the table name forms set published to true and let's double check if the field published is correct so it it is published and as for ID this is where we need e qus instead so forms ID and let's import calls if we don't already have it I believe it's from here yep uh on the forms ID and that would equal to the form ID that we pass all right now we can go back to our form um component and implement the handle submit functionality first import publish form and we would make this asnc so if we do have an edit mode then and not the data we'll just take form. ID but I think the problem is that we also called use form form so this form is actually our form data so it's coming from props uh from here all right let's save this and now if I so I'm on this authentication not knowled survey if I do publish it gets reloaded and let's refresh this one it is set to true the field options are not defined on the question that's because in here we have question select model instead we can have this ex extended um type so we can do type question with options model that equals to this one and even here we can use array of that and that would be the correct type now once the form is published we're going to display a popup for the users which is going to contain like a success match swich that your form has been popular and also a link that they can copy and start sharing so that I'll create in a um new component let's do it also in forms folder new file publish success bit long name and we're going to use a dialogue from which we did previously from sh CN and we need the content to header and a description we'll also add a button and as a props we are going to take in form ID because we have to construct the link that uh we can show it to the users also we need to you know the state of when this should be dial look should be open as well as the onchange function let's on open change it's going to take in one value open and return nothing now starting creating dialogue here this would also be on open change let's do dialog content I'm going to set the max WID for this on a larger screens and let's add a header with the title form has all right let's save this for now and we are going to create create a state here let's import use State and name it success dialog open set success dialog open by default it would be false and we also have to import default all right we can let's close uh minimize this for component and below here doesn't really matter in terms of where we defined it because it's still going to show up as a dialogue in the center by The Styling that it has and as a form ID we are ex uh we're expecting a number okay so we can change that to be a number then we have open equals to State and or open change we're going to set pass the state this change hook so it will let's say if there's a button that's closing it will update our state on the form and for on open change we are going to create a new function constant handle dialog change the all taking aoan and update our state and then we can pass this in here right also I realized that um we're going to modify the handle submit function but what I was I'm doing wrong here is unsubmit instead of just directly passing the handle submit we should be using this uh form function from react hook uh form so form. handle submit and let's rename our function so these two are not confused together I'm going to name this on submit let's save this and uh um so we're going to publish form and then once the form is done publishing we're going to set the dialog to open so that should be it all right we got the dialogue now let's add more data in there I'm going toog description the link below and B after the header we can add a and the description we can add like a input box that allows user that to see that link and also like copy it so for that I'll create a new div class and inside here we are going to have like a text copy link let's do another another div class and inside here we can do input and the button with the text copy so for this input the type would be text and placeholder is link it's a disabled you can't user can change it uh because that's what we want we just want them to be able to copy it and as for the value that would be actual URL so let's get um form and form props form ID so now when we display we want it to be actually like the full form so for that we need to know what the base URL is let's get that at the top constant base URL this we can access um maybe if we set environment variables let's see if that works or like window is usually location is usually how I do it all right I guess that works but during the deployment we should remember that we should add this uh base ZL and has to be public because a client component is trying to access it and since we're here let's uh mark this component on the topic as client let's add some styling on on our input so class name I'm going to add with to be full outline none and background transparent we can also add some styling on the TP that's sits as a parent of this input to wrap this text and the button together so let's color of the Border Flex justify between item Center margin at the top is two padding on the left let's set that to two and rounded Corners medium so that looks a little bit better as for this one let's do class name flex and flex Direction column let's format this doesn't work know think we don't need the separate D for this one now that let's implement the functionality that if I if they actually click this then it would be copied to their clipboard and for that we will need to use Navigator and let's create a new function constant copy to clipboard doesn't take any arguments and taking Navigator clipboard write text inside of text we're taking base URL plus forms and then for props that form ID and then we alert when it's been copied and if there's any errors we catch them copying the function name inside this button copy to clipboard one more thing I want to add is icon at the top and we can use react icons let's just make sure if we have the package already installed um oh it's coming from redx UI all right I don't see it so we can install mpm install redic CI react icons and the icon name is going to be import and we'll add that icon in here so I think the icons from this library is pretty straightforward we just need to take the icon and add some class names for height and the width and that would go before input class name I'm going to do four and add some margin on the right and here's the icon now let's test out copy functionality cliar says it's been copied if I paste that here I'm going to see it's the forms 15 not the edit not the current URL we create this page for forms where users um can go in and fill out the form and instead of just publishing they can submit their answers to it and this will be very similar we're going to use this form Fields form elements just we'll have a different Logic for what happens when they submit and also we'll capture the actual values that they put in so the person then the user that created this form later on we're going to build the admin view so they can see the answers to each of those questions um and I know the form setup itself this part was kind of complicated but this was the most complicated part of the tutorial we're done with it and also the important so we have important pieces out of the place now we can move on setting up other functionalities and features and adding data to the database and saving the data and manipulating it from our client side all right and as I mentioned the form setup would be pretty similar so the URL that we have right now for the edit version is forms edit and for um just a user fill out let's create a new should we name should we do another folder fill out or just new file um I mean form ID and in here page TSX the layout this one would also be same so because it's same we can actually move this into forms I don't think we had any Imports for that so it wouldn't really matter and let's close this out open the page that we had created see so on the right side we see the AL the form I'm just going to remove this console looks we don't need them and I'll actually copy this paste it in here and we're going to make modifications to this so for someone to fill out the form they don't need to be signed in so I'm going to close this out we don't also need any authentication information we do need to still check the form exist otherwise we won't be able to show them the results let's update the import path for the form it would be I think the same well all right so we still need the form ID we'll check the form we'll fetch it and then we'll pass it to form except the edit mode would be false I'm going to save this so if we go here now we see the form but it has submit button instead but if we go to edit forms or wait forms edit now we would have the publish option and let's go back so we still have to create the submit functionality and it will take in the answers and save them but to save the answers we need to store them in the database and as you can see we don't have answers table in database yet but we'll do that in do that in the future parts and since we've doing we've been doing more functionality related things now let's move on and do more more UI styling so it would be a nice switch up uh but we'll definitely add more functionality and um so let's move on to admin Pages where admins of the like creators of the forums they can see the list of their forums and and then the results so for all the admin related routes I'm actually going to create uh an admin a page route and that would go inside the nap so however we create I guess the folder names that always get reflected on the pages the URL so for example forms any page inside form is going to have forms but if we don't want a folder name to be part of the URL we can put it in par parenthesis and that would be called like a page route so we just created a page route and then we'll create a page let's do view forms and we'll have here new file for the page itself so this would be all for all the forms that I have created I'm just going to change name for my now my forms and as you remember for the index page we did create this component I'm going to show you guys if you in case you don't recall that displ the form so we can just reuse this card but instead of fetching all the forms just fetch uh forms for our own so the component name of that one was forms list I believe yep here it is uh let's save that let's add a new action get user forms the typ script it's a server action so we're doing use server at the top and we need to import database so we can run our query also we need equal equation for our wear condition and forms table finally off because um we can on server side determine what the logged in users ID is and we find all all the forms that belong to that user ID so export async function get user forms we don't take in any arguments we access our session coming from O then getting a user ID that's session we check if session exists user ID if we don't have an user ID we can't return any forms but if we do we can run the query save it in user forms so dbquery do forms then we do five manyu and the syntax is incorrect we need to pass where then equals forms the user ID to our user ID and this has to be an object once we get that we return them now let's access this action from our our page get user forms create a new variable we can also Define the type of forms here and this would be infer select model type of and we also need do we need to import forms that has the same name as our forms we're going to import s DB forms from schema all right and a wait and the page has to be acing for us to be able to use A8 let's see what this um this is the array of the forms if we go to VI forms we see my forms and we're fetching this so I can just take this turn into forms list we don't need this save it and we get this one form that I have created let's also add the header do my forms let's just do my forms class name text large bold font weight we are going to add the [Music] same padding and margin to it is now we'll add side navigation bar on the left side here since we're going to have multiple Pages for the admin so uh we can easily navigate between them and for the menu items like the icons I'm going to use um Lucid icons and here we can just pick the name um find the icon that we like and if we do click on it uh we can copy jsx but one downside is as you can see for example if we paste the jsx which I'm not going to do now it has the name like it's not Dynamic we have to import specifically library and um like display that but we want to store like menu items and the names of the icons in an array ideally we don't want to manually Define them so because of that I'm going to go to components and create a new file which would be icons. TSX here we'll list all the icons that we want to use and then we're going to create like types based off of that I'm going to get started with Library adding few more that uh I might end up using and let's also import type icon node as loid icon say react and then this was already install because um shats in UI Library uses it so it should already be there now let's export type icon that equals to Lucid icon and the list of icons that would be Library line chart we have two kinds of settings and finally the list now let's create the type for the navigation item itself for that one I'm I'm going to do types maybe new file types so the name would be SAR and this will contain the title this would be what's going to be displayed on the menu it's required and it's of type string in case it is disabled that would be Boolean external it would open up in a new page this is optional and for the icon we take this is also optional it would be key of type of and icons which we can import here at the top oh this is just an extension that if I have not use for example key of type puff it's going to underline blue and then it's going to explain what is it actually doing um let me know in the comments if you guys want to know which one is it I can May paste the link and then they may have like Sub menu items so for that one we can create a an object so it would either be an object that has a TR string and items that's optional or a TR also string I would be empty and navigation link so let's add T type navigation link so this would be title string H string and disabled Boolean um I think that should be it let's save this and I'll create a new folder inside components for navigation let's do export default it would be a use client we'll need to import link from next link also use path name and sidebar na item type also seeing this would be for conditional um display playing types conditionally and our icons component let's Define the types instead of this I'm going to do interface dashboard nav props and it'll take in an array of items and type would be side sidebar no item array so here we can instead this structure and pass dashboard nav props so so let's get the current path the reason we're getting the current path is because we if we are on that path we're going to have a different styling like highlight so users know where they are and if items are not there then we have no option but to return null but if we do have a a list then we can turn this div into a navigation item going to do items. map let's do constant icon equals icons item. icon and undefined so if we do [Music] have otherwise we're not going to display sidebar no item okay property icon doesn't exist but it does oh items not just like single item okay and then we're determining I'm just looking at the this is copil generated this code so double checking constant uh is active equals to item hre that's correct so if we are on the item's current link and it matches to our current path then that means that item is active so regarding link key let's do index and hre equals so if item is disabled then we're going to do this if not item. HRA but we need to check first that the the link exists otherwise we can't display it so we don't do a classes inside link anymore so I'm going to change that to span and as for the class names I'm going to paste the default class names that I had instead here let's remove this and so what do we have if is active background accent if not it would be transparent item is disabled cursor not allow if not cursor pointer and opacity let's make it less than 100 somewhere 80 so know what's the issue here label is not label it's title for the icon I'm going to do two so usually two four and fragment here that's why we are getting this it's like either Lucid icon type or the fragment is element type so it's um saying it's not a valid jsx element type so instead let's do in case this I items icon is undefined we're going to display a list by default and then now the error is gone and let's create admin layout that would be inside here like usually with layouts we need to pass it uh children first let's return children and here we're going to Define configurations like list of items we're going to have in our dashboard config so this would be sidebar nav field and it's going to be an array I'm going to paste all the fields that I want to have in the array and every item in here has a title a shref and an icon um so for the first one is the one that we just created and the rest are just menu items and um now that we have a layout for admin we can start adding some items first I'm going to wrap this in a div I'm also going to import header at the top um in addition to the side nav bar um so we can have header that we previously created here as well and it would be from components [Music] UI I'm going to rename this to be dashboard navigation and Export default from Peron navigation and n Bar let's also so have a session provider here because later on we are going to add a create form button so we would need to pass in session data for that one and the form generator so that's in this folder all right the header goes at the top so we'll put that first then we'll create a separate div inside this D we're going to had have an asite and dashboard now which we have to pass items that would be the configuration and the array of sidebar na uh the type of configuration which should be what we're looking for for already but Side [Music] bar right navigation types let's save that all right so that's our neare if you H over we have this highlight and currently we are on the my form so this one is highlighted now let's update the styling of this so it's actually uh this in a way the guys can see and after aside we'll create a main class and actually I'm going to move the head header in here on the we forms I know we includeed as a page but I'll add another and let's add Atron saying dashboard and let's update the position of this um like first so we have the sidebar on the side I'm going to get started with name container a main container add a class name I'll make it flex and minimum he would be screen Flex Direction Let's do column and a space on the YX is only let's do six all right so now make uh the one with the dashboard and the main and I'm also going to move this children inside the main after the header all right so this one I'm going to add a class container that should limit the width of it uh it's going to be display grid we'll add some large gaps in between items and grid columns width would be um 200 pixel per one fragment F and flex one so it stretches up the whole uh space let's do so on the larger screen it should look like this and after that I'm going to add styling to the S side so on smaller screens let's have it hidden and the weight set to 200 pixels Flex direction would be column larger screens it would have display Flex padding on the right too let's add some border to the right and also justify between because later on we're going to add another button for upgrading your account telling you how many surveys you filled out uh in the free form so that's our KN bar with the Border probably that's the most noticeable change besides the paddings on the display moving on to the we have flx with would be full it will take uh Flex one column would be it Direction and overflow hidden so as for the header I'm going to do flex and item Center and for the H1 let's do do a larger um font size so text for Excel margin 5 padding for and font would be semi bold and maybe we don't need the my forms header on The View forms so I'm just going to remove that and in the header let's add a session provider and Forum generator button as well let's say if we have here it is I'm going to add some uh line that separates this header from the rest of the contents of the view form and that would be after header HR class name all right so we have a list of the forms now to be able to see the results we need to be able to first uh submit the answers and create submissions by the users and we are going to do that and this is what our database is going to and tables look like after this part so we'll add an answers table it would it will have a unique ID every answer submitted it would also have the value and a question ID so value stores basically the answer to the question and then question ID Maps it to which question it belongs to we'll also create a new table for every form submission so like one User submitted one form form that's going to be a new form submission and it would have which is not shown here but it would have an array of answers so it' be uh have relationships not actually array but relationships to the answers by the IDS and it would also each submission would have a one one uh form um so one to many relationship form can have obviously many submissions schema in our database folder open here that defines all our database models and at the end of this table definitions I'm going to add a new table for the answers and the fields that I have here as I mentioned on the schema visualizer are unique ID and the value that would store the value of the answer for example if the question is uh what is your feedback on something and user types it in we store that value here question ID denotes like which question it belongs to and it would be relation on like this ID then we have form submission ID which is also a separate table that we are going to uh create um and field options ID so let's say if user selected a field for example the options were yes and no they selected that um yes that yes is a field option that's an element um new row in field options table so we have we stored that relationship in here and as for the answer relation so this is what it's going to look like and um it would have uh most only one relation so one answer would um belong to one question one form submission and only one field option is what we have and um so yeah the fields define on which uh we make that connection which is always I use ID so far as for the As for the form submissions uh this is what we're going to have as soon as we Define form submission this um messages the red lines is going to go away we're naming it from submissions we have an ID and doesn't have any other information other than it um belongs to a form and for this tutorial we're not um asking users to sign in to submit the form um so that's pretty much it that's why we don't have a user ID and um to create the relationships for forms uh this is what it's going to look like we have both one and many kind of relationships so for submission belongs to one form but it can have U many answers all right let's save that and uh push our changes I changed my formatter that I had I'm using a different one so that's why it highlighted um all this and updated the formatting um if you guys would like to know my setup for that also uh let me know in the comments comments and the form relations as well because forms would have multiple submissions so let's find that and add in here and that would include many form submissions as well as questions because questions would also have a lot of answers push the changes so our database gets updated and once we have that we should go back to our form so I'm going to click view here that should um oh let's yep that should display my form and opening up the form component as well yeah smaller space it's harder to see obviously but oh we do have console log data here speak on what the data looks like we are using next spell that wrong next o and I'll have this open so yeah that was submitted but you can see the key of the object matches the name on the field so I know that sounds confusing but I'll show you guys don't just try now so let's take an example this was radio group right in here we defined right so here we have so when we pass the name that name is used later later on to update the state so the react hook form kind of handles as when the user changes the value it updates the value itself so we don't have to worry about any of that we just need to give it uh tell reactor form which uh how to save those values so that's where the name comes in so for the first question the ID of the question was as uh coming from database 29 so the name of this one was question underscore 29 so that's how uh it got saved and then the value of this object would be the name that we give to the answer option itself so now if we navigate to the form field the first um question was radio group right so radio group item has a value defined here so that's what's uh defining this one this specific that was marked had an ID of 41 and then it was saved as and ID 41 then I I put in so the next one was the text type input so here obviously we don't have IDs because it's a free text user can type in whatever they would like that's why in here I just have um next off whatever I typed in as a value so uh now how we can store this is the question and we can parse through the data and generate a new type of answer for each of these keys and then their values so I'm going to go to this on submit function so if we have a edit mode then we publish else we do create an aners array that's initially is an empty array and then we create a for Loop to go through all the keys that we have so the first key we can call it a question ID the value itself we can call a value and then we do off object. entries data is what's coming in here the state of the questions and let's uh part get the ID so ID equals question um so we need to have that as an integer but this is uh string so question ID that replace misspelling oh so we're going to fetch that 29 in here and we would add that to the answer so it would be question ID equals questions and for the field options ID this first will check if the answer contains answer ID um and then if not it would be null um so value is what this is so includes so we would replace if not we're going to return now and so the reason this might be of an issues because we can also have type of Boolean in case of a switch so let's check type of value if it equals to string and okay so field options ID we're going to pass it that and actually if we do this equals to null first and then we can do an if statement so if it doesn't look include answer ID then we just need to store this whole thing so that would what we next o would be inside text value and we pass it on text value as in our schema because that is the field that we have for answers it's value and it's of type text now we need a server action to actually store all these answers send them to the database and also create a new form submission and attach create a relationship between these answers and for form submission go into actions folder submit an. TS us server then we import database also import the tables that we're going to need starting with form submissions and as for answers I'm going to import them as DP answers all right let's define the type of props um type of data we will be taking submit so we first need a form ID to create a submission and then we need answers which is going going to be an array of including question ID value string this is optional and so is field options ID is an array let's create the function ising I'm tempted to accept the suggestion but then I know I have to be debugging so maybe it's the smart decision not to data and first let's D structure get the form ID and answers from the data and create a new form submission equal to A8 db. insert form submissions and the values that we would pass would only be form ID and we do need returning inserted ID form submissions that ID and then we are going to access that by getting the this field of the first element in the array um that was from new form submissions now that we have a ID we can start adding ANW since there will be multiple answers I'm going to do db. transaction we're going to map out for constant answer of answers no this should be so await TX it doesn't need execute we just need returning um actually even that we don't need it but we can return it just in case and what do we have here form submission ID that equals to inserted ID and so this rest of the three Fields okay or shorter syntax we could have just done um D structuring so in the end return the form submission I did itself back to our form let's import this submit answers from the actions and so we have props that form that ID and then we have uh expected one arguments but got two oh so oh because it's expecting data form ID and answers oh because this could be null same for this [Music] one have the answers array issue because the value type seems to be unknown okay let's do this let's create a type insert that would be this one right here and we can export that and we can also use it here now let's [Music] import type answer let I convert this and save it as a string okay we can use the router and redirect to a new page uh for Success page so rout that let's do submit success and we're going to create um this route and as for the router we are going to import that at the top from next navigation router equals router okay oh we should call this not inside the loop but outside I didn't realize it was part of that okay let's add a try catch error and that alert uh that error occurred While submitting the form and let's also add a TR catch inste inside of submit answers action um so it would be here console log error and I'm going to put this inside here in okay creating a new form submission and adding answers saving them in the database uh let's switch it up a little bit and instead of creating a server action we're going to create a new API and if you're not familiar with creating API r to next we are going to learn that as well so we do actually have one API that we set up when we were uh adding authentication to our application but we didn't really go into how does it work what can we pass and all this other important parts of the API route so uh we can talk about that now for the route I'm we have to create a new folder kind of similar that how we create Pages the URL of the API endpoint would consist of the file path uh so let's name this maybe old form related apis we're going to add with form and the new folder for new so finally for the logic goes inside route. TS like we have for next o and here route is just like a function but it takes in a respon request and returns a response also we Define what kind of method it is taking in whether it's get post or others we don't need to Define this is a server it's um assumed automatically so we just need to import DB as we have um to interact with the database Also let's uh import the tables that we're going to need and as for answers I'm going to import them as DB answers so export it's an async function and uh to make it uh this endpoint post request we Define it as post and let's add a request it would be of type request and we don't need to Define this type because it's already built in in nextjs and we'll have uh promise response okay uh yep we are returning a response for the data await so this is how we read body data we have to await get request Json and then for just learning purposes we can console log it and as for the form that is not how we create form so I'm just going to delete this I'll name this new form submission let's before adding answers just create a new form so await db. insert then we have the table name then we pass in the value so form ID would be what is it okay form ID would come from data. form ID and here we can also indicate what we want to be returning which would be inserted id id and finally we are going to access that ID which would be the first element of inserted ID so so if this is successful we return status 200 uh let's save that also instead of new response it would be response. Json and we can create this form submissions ID and pass inserted ID ID to it we're testing apis but this is optional because eventually we are going to integrate API to our client s so you don't need to um test but it's Al always good to check uh API before moving on and integrating and easier to debug that way um so we just create a new request and we paste in the URL so our Local Host is running here then we do AP pi and form and new was the path also it's going to be a post request and we should pass the body body I'm going to select row and Json so creating a Json object form ID and this would be 15 now let's click uh send and we should see the response here so for form submission was created with an ID 10 our console L since we did output the data we are going to see I'm going to move my X code so you guys can see um we passed in the form ID S5 okay now let's also add um answers to it and save them but for that since we're going to have multiple answers mapped to the new form submission uh I'm going to use transaction before we return this await db. transaction Asing TX for constant answer of data that answers so the new answer created ID would be answer ID we do a wait. TX let's insert inside DB answers table and the values would be just answer for each one of them and we're going to be returning answer ID that would be the ID from the row of the table DB answers all right there would be for first form submission ID that would equal to inserted ID and rest of the fields of the answer let's save that and we are ready to integrate that inside our form so to make a request we're going to make use fetch API so method would be post headers would be content type application Json as for the body we need to stringify the body uh when passing to apis but in addition to answers we are going to do form ID here which is props form. ID believe form ID equals to oh it's it's just an ID my bad as for the API URL um so it doesn't directly bridge to inside so if we redirect from different page then this without showing the base URL works but unfortunately for the apis doesn't work that way so we do have to pass like the URL and we don't want to just hard code Local Host because then when you deploy it would still try to make requests to the Local Host so what can we do is create base Z that equals to other what you have in so yeah we utilized this before um and then whenever you deploy we deploy application we're going to add this public base URL in environment variables and let me double check if that one's the one we used or all right but for now it's going to request from here taking this and adding also it's not answers it's form new let's try it out have you build next all oh I think it got submitted um we can get the response and response. status equals 200 that's like the status for a successful response don't know why it's oh this should be outside we're going to redirect to a success page also uh let's move this out outside of a a loop so when we're done adding answers then we are going to else let's alert also okay actually I'm going to do kind um right for redirected to form success but page is not found we're going to create the page after let's also double check inside uh drizzle Studio that the answers got added so my studio turned off so I'm going to run it again answers oh here it is perfect let's create this page form success it should be pretty simple we're just going to add UI to show user the message that the form has been created let's create a new folder here for Success so like a thank you message and I'm going to create classing so I'll add a lot of padding maybe a border rounded medium also Max with let's do small we don't need it too large so if I just submitted inside we have that information so if I go here forms oh we don't need props for the this one and um let's if they have some maybe like um message uh that we can use instead so those are the toasts let's pop over this alert it comes with variant so we're going to it's perfect we're going to have success variant in it and I'm going to install first let's copy import statement and I'll copy a this code so it would be success I'm going to update this one we don't need the icon for this and let's update the variant default okay let's do default I don't think they have success since it's not showing up here all right here it it is now moving on to the view results page where user would be able to see the results of the form that they created and all the answers they were submitted um and for that we will need to go through like three steps first would be to fetch the forms uh created by the user and their submissions then second would be create a drop- down picker that would pick the form that the results we want to display to and third would be create a table to display these results and for for the table we are going to be using 10 St table and here is a basic react example which we're going to follow and then modify so that we have the fields that we need and the data um and we'll go ahead and install this table so here's the name of the module inside our project let's do npm install 10 step react table and we'll create a new form folder in this would go in admin's folder and new page inside results since admin has a layout this one would also have the sidebar and the header so this would be results page so first we're going to add the dropdown and then we'll have the table so kind of to visualize what we are doing and if we go navigate to results now have get user forms action that was when uh we created that when we just display the list of forms that user has created and we can uh reuse that so I'm going to turn page into an a sync component I will import get user forms I'm also going to import infer select model so we can type our result as well as a forms table all right and in here forms this would be array of infer select model and equals array um uh we can rename this user form so it's not conf conflicting with the type itself um all right and if there are no forms we can uh display the message that no forms have been found and for the selector we are going to use uh the select component that we have um in our component onon folder from shat CN so it would be this one and if we take a look at the documentation so we have a select here and then in the code we need to pass it a value and the label and for us the items would be the forms itself so we can uh prepare this options in the following format it would be constant select options and we're going to map the user form so um returning label that is form name and the value that would be form ID now let's create a component for the Picker that we're going to pass this options to this is going to be forms Pier um it will be a client component and we will also use a callback for uh a function and the select importing select from our UI components and I'm also going to add a label so let's define the type of options that we are taking in select props that equals to to value that would be number because we have an ID that's of type number and then label um it could be string or null since in the database it's not a required value and we can rename this to be form speaker props and it will have an array of options select props and let's take taking the props just destructure it and before we create the select element let's just m map them out options. map just going to display a label importing this into the page passing in our options did I see a red okay no forms found that is because I'm not signed in we should also add a dashboard link here in the header but before we do do that going to go here and results so I only have one form and that's what's getting displayed all right so data fetching is going as expected now let's create the select component starting by adding class name Flex Gap 2 and item Center then we are going to follow it up on with a label that says select a form and this label I'm going to make font bold there's our label then we have a select select trigger so that's our button right there it doesn't have any options yet and this is two wide so let's do class name limit the width of it all right that's better for the placeholder by default we are going to have a the first form selected so that would equal to options first element. label and then once it gets updated then we'll pass in the value and now let's do content here we are going to need that mapping I'm going to remove it from here closing Dave so value equals option value but because it's a number we have to convert it to string fortunately so yeah we only have one element I'm going to create another form so we have more of that and the ID of selected form we're going to save that in search parameters and something like results question mark ID equals and then the form ID if you have browsed on like some e-commerce sites you probably have notice that when you click on certain filters the query gets updated so in like a similar manner is what we are going to do and because we are in a client component for accessing this query parameters we need to use a hook use search params that's from next navigation uh import that and for updating the routes like updating this search patterns when we change the value of the selected option we'll need use router hook for that path name going back here calling this hook then we have a router from use router and the path name from use pathing so now let's access for my D so wait to get like per parameters is by search params function. getet and then we pass in parameter name and it might be empty by default because for example now when we navigated through results we have not selected anything and that's fine if there's nothing said then by default we're going to pick the first uh form take that value and convert it into two string after this we need the function when option gets selected to create the query string for adding this parameter and so I will be create query string and here is where we're going to use callback what we'll do is basically this will uh memorize the function and it would be dependent on a input which is a search param so if the input gets updated then this will get updated but if not it would be so we're going to pass oh inside here we have to pass a function and then uh the dependency so name would be string value would be also string and um search pars is our input so here constant we'll create a new URL search param this one to string and after that will return it expected two [Music] arguments so let's take that in and on change we're going to pass the function first let's set the value to equal to form ID and when we set a select item because my formatting is not working uh so we'll do router. push and we'll take path name which would be like Local Host slash results plus question mark then we would create the query string uh the name of the value would be ID and then the value itself like let's say for my is 15 it would be 15 should have a unique key which I missed previously and that would be option. value other form and let's update this um card styling real quick so that the view button is all the way down here that would be form card list we make this flex and card header is flex One Flex Direction column okay that looks better add like a dashboard Link in the header this is header inside UI components so if we have session user let's inside here add HRA that it would be view forms and then button I'm going to make variant outline and it would be dashboard okay okay if we go back to the select item instead of it being on change I think apparently select is the only one that has this on value change so if we do value and then the same function that we have router push path name create query string form and then it should pick up the value directly I'm going to remove this do um C select caching concept the other form you can see that the URL got updated and selected for my t16 if we change this back now it's 15 the ID of this one it's time for the table here and going to tack table documentation I had an example open think basic should work for us and inside um Source main folder here is an examp example of a table these are the columns and the datas the rows uh following up and I think it also has this like search filters I'm going to copy this whole thing and inside results create a new file for table we don't need this extra deployment function table going to export that and let's take a better look of what's going on we also don't have index CSS so first it defines the type of the person and person is basically a collection of all the fields that person can have and in this table it's used as the columns so then what we have is actual data so an array of this type person and every element in the array has its first name last name age visit status and other fields so this is an easier example than what we have because for us the fields are actually going to be the questions itself and the submissions are going to be row and then then each each question would be matched to each each answer would be matched to each question by the question ID that's the unique identifier for us um all right so then column helper so this defines the columns so it's just taking a person type and every field in this person and then this defines how one should access each field using accessors uh accessor first name so in this cell we just get the value of it and then this one also has Footers which we're not going to use um but footer is how it's going to search for the information um then we have last name so here if if you can notice the difference that first name as a label is directly using the field as it def defined but we can also change that and we can update the header to be whatever label we wanted to show same thing for age and the rest of the fields are pretty similar um so this where it stores the data it creates a new state and it copies the default data array into the state and I'm not sure why they're using this use reducer to render it finally using the use react table hook it's creating a table instance and it's passing the data in the object then columns and get core model which is coming from react table so if you create it just like in a basic HTML you have a table but in this case this table is constructed using user react you have a head that Maps out this header groups and it's going to display the columns header and the get context function after that we have a body where we take all the rows get visible cell is how we render the cells and the footer which we're not going to use in this example and oh I see there's a button for reender that's what it's for also I'm going to move this data about the oh one one more thing at the top I have to find this as use client otherwise we're going to get an error because we're using um things like hooks of usate hooks and I think they're also for use R table using something that's only client component um compatible and for the fashing data we're going to have a server component at on top of table component that's going to fetch the data and pass it and in the server component we we're going to get the questions and we're going to get the answers and so let's create that uh new file I'll name it res display here I'll import table and on table let's save changes and we need an action that's going to get the form by ID or we can because this is a server component directly Define it here so DB from we also need equals forms as a props only thing we need is a form ID I'm going to take form ID and constant form equals await db. query. forms oh has to be async find first equal statement forms that ID equals to form ID and then we want to get it with questions that comes with filled options because we would have to display the field option as well and submissions I guess getting submissions from answers would be field options from answers would be easier so if we can't find the form actually return null let's just add a table here without passing the data yet to see what it's looking like and import this into our page oh and we have to pass form ID equals it has to be number um so for this one how we access once again is yes I have to pass the value so how we Define how we access this once again search search parameters but this is a server component so the way we access server component the search parameters is inside the prop here and in the page um so this is I'm going to update this instead would be search and this is so type of this could be key and string or just a string array this is defining type based on what nextjs has it's not something I'm writing and what is the issue oh it's not comma so we'll take search parameter and d and if there is no search parameters ID then we by default pass the first ID of the first form user forms. ID so so we'll do s string because we're not going to have and array in there so that's our table with the data and now let's pass our data to the table and we have for this one kind using infer select model and constructing our own types because drizzle doesn't provide nested types import INF for black model also import forms aners form submissions and fill options so we have a type field option that is in for select model type of field options type and that is answer and it also contains um field option same thing for type question the include questions table Yeah we need that because question has multiple filled options after that so we have an answer with theault form submission it would be of type form submissions and with an answers table we got that and finally for table props we taking data that would be form submissions and for our columns that's going to be a question array I'm going to remove this we also so don't need default data colum helper that would be a question so let's remove all this fields and I'm only going to leave the first one as an example that would be ID and we don't need the footer so we would just get the ID of the row and display that as for the data props table props data and up the columns and defining this would be easier for us because eventually we have to add some Dynamic columns in here and what's the error here we change display the ID of form [Music] and pass pass the props from here that would be form the submissions questions so one small change because answer might not have a field option when we're defining here we can make this optional and also accept the value of null so now our types look good okay so we passed data so now we can just close this out and focus on our tables component I know we do have a lot of fields in here but we did assess a lot of like nested items and we could probably export some of those and share within like types file so we don't have to to Define them again but the table format is kind of straightforward once you understand how the fields assignment Works um so now we only have two columns first one is just a ID it's going to start from one and then number every submission and then we have form ID so form ID for all of the submissions is 15 and that is correct because we have selected this form and all of them should be Lo at this form this is just an example of how we can show like a very simple um value but if we wanted to display Dynamic values like we have in case of questions so for example this form is going to have different sets of questions this form is going to have different sets of questions so we can't use the the same column definition for both tables uh that's why what I'm going to do is instead of displaying form ID we don't actually need that I'm going to take in props and columns which as you recall we passed questions as columns and I'm going to map them out we'll have a question and an index and we'll read so we'll take col Helper accessor and inside the row we're going to find the answer for that specific question um on the that column and we're going to display that answer so and we're going to find if the question ID of the answer matches the question ID that we are currently on what is this and this accessor takes in two argument the second would be header um we're going to return question that text as a header ID would be id. two string because by default our IDs are numbers and for cell let's just render the value let's just Define expected two [Music] argument and ID would be that so in results display or let's say that there's a form that doesn't have any submissions user just created a form uh what we can do is if for submissions don't exist we can just display no submissions on this form yet if we take a look at our table we're going to see that we have ID we have questions here and below the answers I insert same thing for all the submissions that's why it's repetitive but it actually Den notes different uh submissions and let's do a little bit of a styling we also have a very long um question name so maybe we can make that sell a little bit smaller so we have a table here I'm going to add some margin at the top and I'll wrap a table in another D where we're going to add like a shadows and maybe a border as well a class name Shadow overflow hidden border let's do border color very light gr 200 and rounded and that's what it looks like so if we had a St stying on table it actually doesn't get reflected so instead we should do um styling on the cells or like rows itself so I can do class name here border at the bottom so that should update the Border here and for the table head class name text let's keep it left and add some padding so we have a little bit more space here let's divide on Y and divide would be color gray 200 once again some pading and for the data same thing okay so that's our like kind of basic but Dynamic table and I'll remove this results [Music] on just a text placeholder text before we added this components page so let's create one and inside the public folder I added some images that we're going to be using and it's going to be also on GitHub so you can go ahead and download them but these are like for example oh I don't know why it's not displaying like demo images I just took like a screenshot and the rest is like SGS for other part and on Landing pillage where we're going to have a hero section A feature section um so yeah let's get started with that inside app I'll create a separate folder for landing page and index the TSX I'll rename this Landing this component landing page and at the top let's import uh form generator like the but we're going to display in the hero section okay let's save this like sample form and I think our main part is page so I know we were experimenting with some things here but we don't need this anymore so it doesn't have to be aing forms we have a page for that also for this landing page add that here we have a header that's great and Main class name minimum height flux [Music] column good to um the index page so we have landing page let's start adding content to it so first I'll add a section for hero and it will have a Tex something like saying create your forms like what is the application about and in between we are going to have a break so it's um spread out on two lines after that we're adding a description I'm going to paste here the text that I came up with for the description and this would be followed by Form generator let's update the classes for this we're going to make the text bigger font would be bold text Center tracking tighter on larger screens text is going to be 5 Exel and then for even larger 6xl as for leading let's do six all right now regarding the paragraph following I'm going to set max width to 600 so margin at the Tor so we have space uh from the hero text would be centered also so color I'm going to do a little bit lighter than it currently is some [Music] responsiveness so we also want to Center this so let's update in the section styling make it Flex Flex Direction column item Center also justify Center for padding at the top I'm going to make a huge padding with full but this one let's do four and then on larger screens and for the background in um Tailwind if you want to add the background we can just pass the URL and then the path of the file but it take not that it should be coming from a public folder that's where nextjs can read assets uh from um it's like a light um opacity grid that we have here and the grid cell I generated from uh this website let me know if you guys um are interested so you can pick different patterns colors and change the overall opacity so that your text I guess shows up if you're writing text over it shows up better um so I can share this link um if you're interested also I have a list of tools that I use like for example where I get my icons all design assets and all the other things collected in like a page so I'll add the link um in the description of the video as well so check out it will maybe help you to make your web development faster and um yes so we're going to fix um this spacing here um but before that I think we're not adding anything else to this section so let's make um I don't want to have like this like cut off in here I want it to kind of uh slowly Fade Away um I think it'll be better rather than just maybe creating a line or something so that we can achieve with adding another layer and using um tail class gradient and we can do that uh here with full background would be gradient so starting from Top top to bottom and it would start from transparent and and to White so like have this fading away effect and we set the height of this div close the div and as you can see kind of Fades away here although it shouldn't have this padding let's check where it's coming from oh it's the main class pading okay I'm going to remove that I think I missed it we have Source inside [Music] app okay great so that was quick and let's add another section that would be for showing off the features I know why suddenly my brightness went too really low um okay so we have a section we'll use H2 and it will say how it works the features we are going to add in an ordered list and list items are going to be images with the text so some so here's the list and we'll also add images that I screenshoted and showed for the demos it's going to be images is app demo [Music] 2.png we should set with and height they're Square images so I'm going to set them to same alt text would be update the form class name background white padding for shadow small for to like highlight the image because it might look like part of that section um yep let's save that that's the first one we have one for each one of them oh sorry this one was demo one [Music] so that's all the points we have let's display them side by side and we'll add a grid to our unordered list container G Gap four smaller screens it would be one column on medium screens and on larger three x with 5 XEL text Center so we had the smaller one open that's why I displayed it like that and for now let's update the list items itself this one we're going to make a flex direction would also be column items and and I'm going to make it relative also because later on I'm going to add an arrows and then the arrow would be absolute so that's why and then this how it works for the section as well some margin at the top and big p heading at the bottom and I add an ID so this would be features this would be hero I think the first one doesn't really need an ID but still and I'll copy the H1 class name just modify a bit let's make it smaller this is fine we only have one line and I want to add arrows from going from number one to two and then to three so it kind of um shows like a flow more smoother and uh I'll add it inside um list item another image that would be of an arrow Source equals to arrow. SVG and the width of this would be 125 height alt text arrow and so class name we have to make it absolute because it's kind of not inside the list item goes outside of it absolutely in regards to list item so for top it would be on y AIS it would be in the middle then from right let's do zero and transform translate X like 50% and negative Translate Y also 50% so something like that and we'll take this and flip it on the other side so from here to here for the other one and to do that turning around we can use scale x minus one and rotate so that's going to turn and rotate 180° so the arrow head is on the other side here now we are ready to move on to the payments and for payment payment processing and subscriptions we are going to be using stripe and the way it would work is that maybe we'll limit users to creating certain amount of forms for free and after that um they would have to upgrade um have a subscription instead um and um stripe does come with the package which we're going to install and from there we we're going to add like API keys and also what we can do to incentivize users to upgrade their plan is have this indicator on the sidebar which shows how many forms they have created and how many free forms they have left um so for that I'm going to let's first do that and then we're going to go to the stripe create the keys that we will need I'll close this table related stuff and um we're going to add this button in the navigation so I will also create it in there let's name this up account so we'll need link for this and if we go to never mind and we need to count the user forms so we're going to utilize the action that we created previously we'll make this async and constant forms equals get user forms and then let's do form count will just be the length of the forms we can um save somewhere like maybe in utils file uh how many forms we have so export constant Max free forms I'm going to do three but it's the constant so changing this is pretty simple Imports the number from uh okay so we have that and let's calculate a percent so it would be number of forms divided by X3 forms and time 100 and then also later in the user scheme model we're going to add like a sub if user is subscribed or not we're going to save that information when we create payment so we'll not display this if they're already a subscribe user um and then let's do form count out of Max free forms generated and I'll do another one H let's do settings page I'm going to double check if that's what we named in the menu and let's do [Music] underline upgrade your plan and this space for unlimited forms inside the layout so let's go where we have the layout of ADD me and import and here we also have justify between so it's going to show up at the end of the page and here we are let's create a progress bar so we can also show this component that could be reused we can just just create it directly in components and it will only need uh one props which would be the value and that would be number so we're going to return two d one would be the full background uh which we're going to set it to gr and another one would show how much progress uh is based on the value that it's passed so let's do with full background gray I'm going to do 200 also round it full height I'm going to sent it to 2.5 and that should be it for the outer div and for the inner we're going to make the background primary uh same height round it and we need to add one more uh thing which would be the style and it would be width and the percentage would be determined by the value so if we take this in here and we can add that above the text by passing the progress so I have two out of three oh I did the value but at the percent okay so then it takes up 2/3 of the space and let's add some paddings around this text and a margin so not on the progress bar but in here padding for margin bottom or also for and small text actually I'm going to move this above all text and there we have it going to stripe in here I will [Music] create inside developers API Keys both of this publisher key and secret key that you see here we're going to uh put them in the environment variables and the name of first one would be public this is publishable key so it's okay if we use it on the client side I'm going to paste my value here and for Stripes Secret okay but to note here the name has to start with next public other otherwise the client side won't be able to read it let's save them and this um customer portal where they can change like their payment method with the subscriptions or upgrade their plan if they don't have any and um if you want to know in details how stripe uh works then you can check out uh the video I have about stripe where I go into more details um on the process but to briefly overview we will first stri creates a session uh we do that using their module and once we have the session we submit the request using their Pages like check out as you can see here it sends a request to perform the payment and then there's another API that needs to check if the perfor um if the payment has been processed uh but it's not a direct like one API call it's like two steps on the way and to monitor U the payment processing status we use the web hook so when the payment is done processing then stripe triggers the web hook so in case of a e-commerce website that's when you would start your shipment for example once you have that act confirmation that it's been paid um and yeah let's install their module mpm install so adding two modules stripe JS and just stripe let's add that and inside our utils folder uh we are going to create a new instance of stripe that we're going to export into wherever we need so import create stripe uh. typescript then import Stripe from here export Conant stripe because every time we need to passing like um keys so it would be say that in we stripe Secret key and we need to check if it exists if not then empty string and for in here we can give it an API version okay our first API which would be U checkout sessions I'll create a new form folder for stripe and then another folder for checkout session here we can add our routts we need to import stripe first from all Library we're also going to need off to get the user ID and that should be good for now so uh this is what we're going to be doing create a session uh let's see if um let's see like the noj version so we load um stripe this is what we did when we ped the key and then we call this function stripe checkout sessions. create I'm going to copy this sample code um success ZL is where it will redirect to if the payment or like the checkout process was successful then we have price and quantity and also the mode so these are some of the required parameters and then there's some like customer email that we can add um and it's optional and there's actually bunch of others so let's uh get the user ID first export a sync function and this would be a post request request of type request as for the price that we are going to pass from the body so it would be quantity uh we could pass this but by default let's make it one because we only have one product and yes that's it a wait request at Json no constant user session that equals to and user ID would be user [Music] session Giver an ID and now it's type for for the session change the mode here to be subscription the payment was for uh onetime payment uh quantity we're going to pass that same thing for the price and um missing comma here as for the customer here you can see that uh customer reference ID a uni streak to reference checkout session this can be a customer ID a cart ID um okay so that's not what we're looking for so idea of an existing customer we can pest that as a customer user ID maybe it's a separate okay as for the success page the first part has to be our URL so as we did in um previous examples to fetch the base URL I am going to use that and let's do payment success I also want to add payment method types to be card only and once we're done with the session we can return the session so this would be session return new response Json stringify uh let's get that session ID and the status would be successful 200 else we would return your response um saying that we failed it failed to create a session status can be 500 to be able to call this API from the client side we'll be needing to implement uh Stripe from client as well so for that like creating object I'm also going to do it in this folder here and let's name this uh stripe typescript and this will be set up so this time we are importing from stripe.js and we are creating a new promise that's going to create load stripe and it's an promise because it returns it's calling the stripe API so um doesn't return the response right away and we are using our publishable key in this case and and that's it for this file we can save it we go to our this um upgrade your plan link we can instead redirect this to open up a stripe checkout page but the problem would be there that this is a server component and for stripe client we would be needing to create a client component and um so we can it here we have to create another component so that's like one of the frustrations that people have with Nexus and then new model of this client and server components some I think it's redundant some I think there's not clear patterns to follow but i' would like to know what your opinion is let me know in the comments and since I guess I'm using xjs I'm trying to find ways to learn most about it and how to best use use it since this case it would be a decision to be made to whether create a new link that would I guess have a client component with loading all this stripe checkout but then that would need user to click from here to another page again so I think it's that to just directly redirect them from here and that's why uh let's create a new component upgrade account I'm running out of the name because I named this one this but it's okay we can create a new folder for sake of like keeping all subscription payment related things in the same folder and new file for this would be Subs so it would just literally be a button that says upgrade your uh plan and let's do class name underline like we have in its parent container so this I as I mentioned would be use client we'll also import get strip and use session and sign in we're going to double check if the user is not signed in then we would redirect them to page and the use session as you remember inside a component that's wrapped in a session provider so what we have to do is import a session provider here I'm not sure if although though we can do that in a oh I have an idea so as props let's get user ID that would be a string and also the price not the ID and import us router so if user is not logged in although they should be we're going to redirect them to the signning page handle check out this is going to be a synchronous and it would taking the price and if there's no user ID let's go to the login page and now let's try creating a session we are returning session from our API so method post API stripe check out session so we don't need to pass the user ID just the price because we're fetching the user ID then again over there and after we so this won't work let's console log just in case to make sure we're getting the session ID and then create stripe get stripe and if stri pist redirect to check out which then will take the session ID and pass to it and all this will happen on handle checkout button click equals handle checkout oh I have to make a call to it and pass the price let's import subscribe button in here instead of this one and we need to pass it price significantly worse over time or maybe it's just me session user ID we are looking for so let's make this optional here because we're already checking if anything we're going to redirect them to sign in next router was not mounted maybe I imported it from a wrong place yep it should be navigation let's see if we get session ID first Local Host API stripe session oh so actually not passing base works I don't know why last time I tried I think it was the version before this one didn't work for me the I'm talking about nextjs and for for the price ID it should be um for the price itself it should be the ID of the price and that's part of so if we go in this um documentation we are going to see how to add a product in the dashboard so premium product and then it's going to return us the price ID that we can use on our client side AI form generator um start standard pricing and let's do 19 USD monthly um okay so if we do save product uh this is where the Mr term I guess comes from if you guys have seen on Twitter when people creating Products Post their monthly Mr lot of the screenshots are from I believe I should not have copied that this is what I'm looking for or from um stripe okay um and that could be you after this tutorial uh we can also maybe like store this in the database also this is string let's see what the stripe recommends is the be best practice create a check out [Music] session don't think it's as much user ID just yet because the customer f this for existing stripe customers the ones that have used striped so later on before we add it to stripe we're also going to add subscriptions but for now let's just purely test stripe integration here we are going to see that we're getting redirected to the checkout page so it shows the name of our product here later on you can also add an image and more about it the price of the subscription and and here is the check out form with the card information and uh it should redirect to the success page let's create success page first and then test uh this checkout form and then redirection also see what um a session looks like and for the this information you don't have to actually use your card there's like a we are in testing mode now so there's like a sample card numbers that we can use we add adding these two fields to our customer first would be stripe customer ID and uh that would be if the user has been a customer already then we will create ID but that would be using stripe so we can pass that ID to stripe and later on when the payment is confirmed we can update their subscription so subscribed is the field that I'm going to use both of them are optional and this would be Boolean so now going here where we have user ID we can check using this user look it up in the DB and see if they have a field for customer strip customer ID but first let's push our changes to DB all right and I'm going to import database here we also going to need equal from trle RM since we're looking up the users and a users table here we can run the query so first if there is no user ID we are not going to let the payment go through and then this uh DP users find First and not the users but query okay and this would be and where condition we pass it as an object so if we have an user create customer and if user has a stripe ID then customer ID already then we're going to um use that one so we'll set customer to user stripe customer ID if not let's create a new customer using stripe to save the customer that's the function that stripe um has customers create and it takes in this fields that are defined by stripe we do have access to email but that's about it and if we do want to pass any custom data for example the ID is what we want to get back from stripe response when the process has been the payment has been completed uh to identify the user right but that one goes inside of metadata and I believe they yeah uh they have a here set of key value pairs that you can attach to an object um and this can be used to for storing additional information about an object in structured format uh but if we try to add it as one of the fields then we'll get an error so okay let's get into that um just going to copy uh this part and let's prepare a customer data object so if we don't have a stripe ID we are creating one and that equals to and here we're going to add um DB ID which would be user ID not to confuse it with the other ID that stripe is providing um and we can also pass an email which we fetch here it's a user email okay so C customer would equal to await stripe create customer data oh we have to define the type so for the email I'm going to make it optional or null oh but I don't think we can pass so let's um skip the email because only thing that we need to identify the user is ID itself and we'll set this as ID so this would go in the lse statement find we can't um pass it as at the field so we're just going to make it equal to an object that has a field of ID and user stripe customer ID okay and then here this returns and ID so we can do response is equal to await strip customers create and then customer equals to to response. ID all right and let's that ID okay you're passing this customer ID to the stripe we can also store that in our database so await TB update this is I think first time we using update from drizzle user so it would be a set only field we are changing is tribe customer ID they're not subscribed yet that would be uh just customer ID and the we field which would be users. ID equals to user ID okay let's save this now I'm going to go back try to check out again okay let's see if this got updated and here we have the customer ID great now let's create the success page as I mentioned I'm going to make it look very similar to when user publishes a form we have a success page there when user submits the form it's inside form ID and here we have it and the stripe URL is payment success so let's and we'll also we can add like a link to the dashboard has been updated link HRA this would be view forms go to to the dashboard let's do this one underline to create more forms okay test payment methods let's see card information this should be three number and one two three four five okay so it got submitted and we do see this um success page final part of setting up payments is to uh create this web hook endpoint for the stripe so in case an event changes stripe will ping this endp point um and tell us like information about the event for example subscription cancelled it failed it was created confirmed or there's like a list that it provides and these are the examples and then once we create the endpoint we have to go in the stripe dashboard and pass the URL to it and then for the your production URL but obviously for a local host uh it can't call Local Host it's just Local Host is running on our machine so for that we're going to like listen to it instead to test it out just locally and here's like a sample code of what's going on so you define the URL and taking request at the body and there's um uh we should check if it has a secret defined and taking the signature then using this stripe web hooks we construct an event and um taking read the event based on what is it uh provide logic to it so yeah let's go ahead and create this inside API folder I'm going to do inside stripe new file web hook of new folder first and new file route the typescript so I'll import um stripes we might need some types from them also our instance of stripe export a SN function this would be a post request Quest oh there is some sample code that GitHub compilot provided and is actually does give us a head start we the web hook we can fetch it by going back to the stripe dashboard in developers and then web hooks um tab here we can add and point also you can do this when um deploying the application because here endpoint URL would be our deployment versal app is just simple we have to update it later but the path would be API stripe web hook uh for the event you can select uh a specific event or all of [Music] them so let's do all the check hun related um and a subscription all right that should be it and we rebuild the signing key we're going to copy that web hook secret let's add that and I'll paste my values so so let's say if we don't have a process in we stripe web hook we're going to throw error and it would be this message right here all the events that we're going to add a logic to in here that would be relevant events New set check on set checkout session completed uh we can also do customer subscription deleted and um create and so for the two events if the subscription has been created we're going to Mark user as subscribe if it's been been deleted we're going to change the users filled on the users table uh to false and for that we can Define actions I'm going to do new file user subscriptions for all the subscription user user related stuff and import users from the schema also equal from drizzle and we don't need stripe on this one um so create subscription and we need to take in user ID and that would be of type number update users uh we are going to set subscribed to True oh ID we're using text right okay and there will be another one similar to this but this time it would be delete subscription we're going to mark that as true let's save that and we can create subscription delete subscription from app actions and then also if the signature is correct we return null that's for safety reasons and let's check if event type matches the events that we want to take action on wa create a subscription and would be getting user ID from data object and let's First Data so stripe is returning our customer information right so we're going to Define this is stripe. product and this would be data. customer break oh not product the subscription custom that is a string okay string is not assignable the word case okay let's add another case for deleted this would be delete subscription and default if we don't have the list uh event in the list to handle then we're not going to do anything about it and return received true uh where am I missing probably here okay let's save that he's already subscribed so we're going to update the code so this won't um show up but they want to change subscription we're going to add that functionality inside settings here and that would be tied also we're going to use Stripes create uh this like portal uh function it takes the ID of an existing customer which we can look up in our users table we have access to the session um and yes so that would be in the API I'm going to go back to our API folder where we have this stripe folder and let's [Music] do create portal new file file route. TS we need o we need DB users table also stripe uh that should be uh I think that's it for now post request then we look up a [Music] user so we get back session session. user. ID if we don't have a user ID we're going to return new response and we can send the error message if not let's get the user for database so that would be db. query users find first where and equals users ID to to user ID okay let's check also if user exists so if not a constant stripe customer ID equals to this and let's say we don't have this created either because there's a chance that they never I guess clicked check out and then the first time they're just clicking on settings they want to update so in that case we have to do the same thing we did here which would be when we look up the user we create a customer and if not we take it back from and create and then we get the response from this is calling this function here we pass the ID of the customer as well as a return URL that would be actually let's take this space URL if we are on Local Host then we are going to use Local Host URL and instead of account we have settings page so after this is done return sponse json. stringify URL [Music] and Status 200 create a settings page I'm just going to create directly page in here and let's make this use client because we're going to have a pattern for subscription that we're going we're going to make an API call to the API we just made for create portal and once we get the URL we're going to redirect user to the URL so we'll need use router from next navigation and we'll also add a button from components and use session just to display like session information to the user above the button all right let's make sure our navigation link is correct and I'm going to move settings to [Music] admin because we want to display it into a for it to have the layout as well and actually let's make the button let's create the button separately and for this one we are just going to leave it as a server component so it would be [Music] asnc so if we don't have a session we're going to redirect them to sign in and let's create a new file for manage subscription import use router from and a button as a props we'll be needing user ID that is a string let's the structure that actually we don't need user ID change your subscription equs to use router uh let's save this first I'm going to import this button here get the user account information if they're subscribed or not users find First and equal okay and the plan would [Music] be user. subscribed then we have premium if not uh free so let's add that so add a header let's navigate to the setting page um I forgot to add use client here let's just add a larger text [Music] for and some [Music] pading maybe a border and rounded okay going back here let's create a function that will redirect to customer portal it would be an asnc function so let's see constant response await fetch API stripe then we have create portal it's a method post content obligation Json stringify we don't need to pass any data here I'm going to remove the body and a catch statement all right [Music] try then we do router. push [Music] URL add as on [Music] click Let's test it out we can create portal session until we save the settings so going to this link is uh activate test link cancellation so we allow users to cancel let's do immediately and save changes also subscriptions we are going to allow users to change switch their subscriptions um cancellations this we select which um plan and let's do so payment method is turned on this returning object here from response is an object and it contains URL so let's push to that instead and change your subscription now we get redirected to this page built by stripe um I subscribed twice so I can can cancel the plan there's even a feedback at the end of it great so I'm going to cancel this one too and I'm going to go back now let's upgrade the plan subscribe all right this should trigger a web hook so event check out s was completed and you can see that there is one pending web hook where stripe is still calling a our um API but because we haven't it deployed there isn't a way for it to call it unless we set up Stripes CLI home BR setup then you can run if not you can follow uh this link uh on learning how to set up it's very convenient to install like packages uh globally I also use that so I'm going to copy this command and anywhere in my terminal if I paste it it will start setting up for me um downloading that for me and then we would have to log in into the CLA or maybe API key would be easier instead of loing in with an account so I'm going to try that but first we need to let it finish downloading otherwise this command stripe wouldn't work in the terminal and it will ask you if you want to access give access to the CLI and once you click yes then you can use the CLI um using this listen forward the web hook event to our Local Host so I'm going to run listen to and we're going to copy our Local Host API stripe web hook oh one thing instead of user ID I replaced everywhere with stripe customer ID because that's what we have access to from when stripe sends us a request so in this functions we're going to pass stripe customer ID as well the rest Remains the Same currently the secret that it outputs in the terminal it's only for the session from Local Host while you're listening so I added another variable so this would be only for local host and I changed in the web hook uh stripe Local Host and then we are going to revert it back to uh like just webook local for deployment and now when I added uh so before I was getting 500 errors saying that signature is not uh matching but now we got the status 200 so it was stripe was able to send and communicate with our API successfully and if we go to events let's do change your subscription there's a one web hook that's pending so if we go to terminal we're going to see that I also added a console log of data coming in from stripe and in this data I get back the customer um ID so that's great I'm also going to do just in case now I am going to subscribe and I did see that we received the event type check out session completed we're waiting for the check customer subscription created and then creating subscription so use a subscription function kicked in if we go to drizzle reload we can see that subscribed is said to true so great this is all working um since we tested locally the web hooks I'm going to remove this console logs and update the URL uh we should get ready for [Music] deployment okay let's hide this button if user is subscribed for generary index we have use [Music] session uh let's create a new f a new action inside so this could be good user subscription uh user ID is what we have access to in our next us session which is string and no we are not [Music] selecting TB uh do query. users and let's do constant user equals to and return if user is subscribed or not not the form generator but this one which is [Music] in layout and here we can import get user subscriptions so let's okay so now since I'm subscribed it's not going to show up here and we need to also disable this if the user is not subscribed there is a client component we can put it inside and we need it to be Cent component we'll put it inside a server component that fetches the user subscription data um and displays it accordingly so we can do new file think as a props it takes in children let's import get user subscription so first [Music] session get subscription and then if subscription exists then we can display the children meaning we're going to let users to create forms okay that was interesting so then if you also need to check if user is under three forms then it's fine for them to create uh check how many forms have they created so forms user ID equals to user ID and this would be count so if this is less than Max free forms return children else we can return disabled button and upgrade to create more forms let's add a icon here as well maybe a lock one see react height four margin right [Music] two and the plus sign on the generate form form generator index now it is time for the deployment and we'll be using Verso to deploy our project and what I did is that um you can add your code on GitHub and then you can um add new project and select your git reposit repository so it's going to fetch um logged in with my GitHub account and it will fetch my repositories then I'm going to select that one that I want to import change a name make sure that framework preset is set to nextjs since um that is the framework that we are using and uh before the deployment the step is to add environment variables because we have not committed them to GitHub these are safe on the en file so we can go to the en file and start um copying all the environment variables we have but the ones we would have to update would be Google client ID Google client secret because uh the client ID and secret that we created the Callback functions for them is a local host URL so once we deploy and then you paste your value here and get the URL we're going to take that and create a new client ID and secret and also we'll use the URL to add next public URL so just paste all the environment variables besides the Google ones and the public Ural are the keys that I have right now and for the database I do recommend having a separate databases one if you're going to be adding any features in in the future one for production one for development because just in case let's say you update a schema that might delete some tables um accidentally and that happens in production you want to avoid that while that is deploying I'm going to go back to Google cloud and go to the project where I will create a new credentials and this would be once again again of client ID right and before that we are going to paste the deployment URL here right our application is ready we can see a sneak peek here and let's instant previews I don't see the link let's continue to dashboard uh here is our domain so I'm going to copy that paste before this API o callback URL and let's add back to to settings environment variables we'll paste the client ID the secret we just generated so this is what I have and final one would be the domain of the deployment which I'll past it here and the name would be next public base URL let's save that now since this was deployed with the previous set of environment variables and we added them we can always go here see the deployment see their status and click redeploy and it's going to tell us that the source code is the same but we have new configuration so we need those here out our production instance I'm going to sign in here go to the dashboard there are my forms then we have a results page and finally settings and for in terms of analytics uh there's an option that you can enable web analytics inside versal it would give you information about um traffic number of visitors um or if you want uh more I guess comprehensive solution I was planning to try this one out for this project um the reason that I picked plausible over I guess maybe Google um analytics is because they're open source and let's say if you wanted to uh host it using your own infrastructure and manage it you you're feel um you're free to um take their code and do so they have a lot of also um good reviews uh and but they are um this is what the pricing is looking like uh if you're interested in that and they do offer a 30-day free trial which is what I'm going to try setup I'm going to add a domain here so it does provide uh this script but it does also have an xjs separate module which is going to work better with nextjs let's go ahead and find that [Music] one here okay we need to install next plausible npm install so I wonder if this domain is required to pass okay are this is their documentation on GTH Hub the next plausible package we're using App directory we can add this in a root layout which is in the app folder so I'm going to copy this and it goes inside the head so I'll copy this whole [Music] thing we are going to extract this save it as a environment variable so you guys can e easily modify uh let's name it plausible domain and then this layout is a client of no server component so we don't need to add next public in front of it I would say or like this I'll copy that one that equals to this one right here because in their script let's see settings that's what they shows it's like the generator we can copy this and create a hook for that one and we will throw it inside the button click create form so this is a good way to collect data for example if you collect the views on your page and you see that a lot of people click on the button but then they don't actually generate the survey then you know that you have to optimize something instead of a from a popup um or if let's say they don't click this button at all then you know they have to work more on modifying your landing page anyways we first have to commit this redeploy and then we can uh try this out well I push my changes also adding this in environment variables on the verel save it reload this page you guys can see that that I did have one visitor let's see if it so now it's two page views and custom events I believe we can set up the cool thing is about you can set up goals and then put your number desired number and like track uh so let's add the goal custom event create form add goal all the way down we are going to see uh the goal how many times we had like conversion in this case I just clicked create form once um so that gives us this number wraps up the analytics piece and don't forget to update your endpoint URL for stripe web hook with your deployment URL so if you go to the developers dashboard inside web hooks click on the added URL final update that we're doing we can go to the settings update details and this was just a placeholder that I put before to show the setup so I'm going to paste the domain URL for our rsal deployment and then this uh p would remain the same and with that change we have come to the end of tutorial I hope you enjoyed it and it was helpful I know compared to my other tutorials this was a long one but if you liked it and if there was something new that you learned I'd love to hear what was the topic that you discovered in the comments and I'll see you in the next video thank you for watching