
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, we dive into creating an OTP verification system for hospital admins to log into the admin interface. We explore how to set up a passkey modal and implement verification logic to enhance security. The process involves defining component behaviors and managing user access through React state and environmental variables.
00:00:00 So now that we've laid out the foundations with ChatCN, Tailwind CSS, and general styling, we are ready to start building up our homepage.
00:00:08 To get started with our homepage, we can give this div a class name equal to flex to make it a flex container, h-screen to give it full height,
00:00:19 and max-h-screen to also set its max height to 100vh.
00:00:25 Right within it, we'll want to display another section.
00:00:30 And this section won't have a scroll bar.
00:00:33 So we can give it a class name that's going to say remove-scrollbar, set it as a container, and give it a margin Y of auto.
00:00:44 Now these two classes are not coming directly from Tailwind.
00:00:48 Rather, they're coming from our globals.css.
00:00:51 So if you want to learn more about what a specific class does, simply hover over it and you can see that it will apply some widths,
00:00:59 margins and paddings or search for it.
00:01:02 And you'll be able to find it right within the globals or within the Tailwind config.
00:01:09 Great.
00:01:10 So now that we have this section right within it, we can have another div that will have a class name equal to sub dash container and a max W so max width
00:01:26 of I found the value of 496 pixels to work the best.
00:01:31 And within it, we can display an image.
00:01:34 This image will, of course, be imported from Next Image, so we can get all of the image optimization things out of the box given to us by Next.js.
00:01:44 And here, we can render our logo.
00:01:47 But we don't yet have access to it.
00:01:49 Like we don't know how our logo will look like.
00:01:52 So going back to our design, this thing right here is what we're developing right now.
00:01:57 A left sidebar with a form and the right image right here.
00:02:02 So we need this Care Pulse logo.
00:02:05 How are we going to get it?
00:02:06 Well, you can keep selecting it until you get to the logo mark and then you can just export it right here.
00:02:14 And sure, that's simple enough, right?
00:02:16 You just download it and that's it.
00:02:18 But just so we don't have to download every single image right here, I have already exported all the illustrations, icons,
00:02:25 and more from this Figma design so you can get them all in one package.
00:02:29 You can find them within this GitHub Readme.
00:02:31 If you go below the snippets onto the assets, click here and simply download them from Google Drive.
00:02:38 It might tell you that it cannot scan them as there's some viruses, but don't worry about that.
00:02:42 I'm not trying to hack you.
00:02:44 I just want to give you the actual icons.
00:02:46 So once you download them, simply unzip them, remove the existing public folder, and then drag and drop the new public folder right to the root of the
00:02:57 directory and say copy folder.
00:03:00 And you'll be able to see a public folder with all the assets.
00:03:04 Great.
00:03:05 If you have that, we can give this image a source equal to forward slash assets, forward slash icons, forward slash logo dash full dot SVG.
00:03:16 We can also give it a height of about 1000, a width of about 1000 as well.
00:03:23 And we can give it an alt equal to patient and a class name equal to margin bottom of 12, age of 10, and w-fit.
00:03:35 And we can save it.
00:03:36 Finally, if you go back to localhost 3000, you should be able to see something that looks like this.
00:03:41 Pretty simple so far.
00:03:43 I know, but soon we'll add an entire patient form right here.
00:03:47 So let's actually go below the image and let's create that component, which will soon become our form.
00:03:54 We can go to the components folder, create a new folder called forms.
00:03:59 And within it, we can create a new file called patient.
00:04:06 In there, you can run RAFCE, which will create a basic React arrow function that looks like this, allowing you to simply import it right here by saying,
00:04:18 patient.
00:04:20 Coming from components, forms, patient form.
00:04:24 Going back, you can see a simple text that says patient form, which we'll update soon.
00:04:29 For now, we can do the rest of the homepage by creating a new div below the patient form.
00:04:36 And this div will have a class name equal to text-14-regular.
00:04:44 And for now, we can simply give it a copyright sign.
00:04:47 I typically just Google that.
00:04:48 So copyright sign copy.
00:04:51 That's going to be this symbol right here.
00:04:54 Copy it and paste it here.
00:04:58 Yep.
00:04:58 That's the one.
00:04:59 2024 care pulse.
00:05:05 And you should be able to see it here.
00:05:07 Let's style it better by giving it a margin top of 20. Flex.
00:05:15 justify-between, and we can actually put this within a p tag.
00:05:22 So copy it, paste it within a p tag, and give it a class name equal to justify-items-end, text-dark-600, and on extra large devices,
00:05:38 text-left.
00:05:40 Below this p tag, we can have a link component, which we can import from next link.
00:05:47 It will render a text that says admin, and it will have an href pointing to forward slash question mark admin is equal to true.
00:05:58 with a class name of text-green-500 to indicate that it is clickable.
00:06:05 And finally, after we exit this entire section, we can render our big image.
00:06:11 This will be a self-closing image with a source equal to forward slash assets, forward slash images, forward slash onboarding-img.png.
00:06:23 And we can give it a height of 1000, width of 1000, and I'll tag equal to patient.
00:06:32 Class name equal to side-img and the max W of 50%. So it takes 50% of the screen.
00:06:42 If you do this, you'll be able to see something that looks like this.
00:06:45 We have a great welcoming doctor on the right side indicating that we can trust them with our information.
00:06:51 So what do you say that we focus on creating this patient form?
00:06:55 Next, we can go back to the homepage and believe it or not, the homepage is completely done.
00:07:01 The only thing we'll add later on is the OTP verification.
00:07:06 So I can add a comment right here to do, and we'll make it OTP verification.
00:07:14 also known as the passkey model.
00:07:18 What I'm referring to is this thing right here, where if we want to join the admin panel, we're going to have this verification.
00:07:26 But more on that later, as for now, we'll be focusing on the patient form.
00:07:30 So let's navigate over to the patient form.
00:07:33 To start working in the patient form, we have to set up our forms in general.
00:07:38 So you can think of this lesson as a masterclass in creating complex and reusable forms.
00:07:45 We'll be using ChatCN and they out of the box leverage React Hook Form and Zod.
00:07:52 And they say themselves, forms are tricky.
00:07:56 One of the most common things you'll build in a web application, but also one of the most complex.
00:08:02 at least if you want to do them well.
00:08:04 They have to be well structured and semantically correct, easy to use and navigate using the keyboard, accessible, have support for both sides of validations,
00:08:14 and they have to be well styled and consistent with the rest of the application.
00:08:19 So let's see how we can build a form using React Hook Form, Zod, and ShadCN's form field.
00:08:25 What we'll do is we'll move over to the installation part.
00:08:30 So let's copy this part right here, and let's install a ShadCN form.
00:08:36 Immediately after that, we'll need to create a form schema.
00:08:40 So let's copy this part right here and add it to the top of our application.
00:08:45 First, since we're using forms, we'll have to have some browser keyboard interaction and some events which have to be fired,
00:08:53 which means that we have to make our application run on the client side.
00:08:57 We're importing Z from Zod and Zod is used for validation.
00:09:02 So here we can define the schema of our form.
00:09:07 Next, we need to define the form itself.
00:09:09 So let's copy everything from here.
00:09:12 Let's copy it all the way without the last curly brace, all the way to the top.
00:09:19 And let's just override everything right here, including the patient form.
00:09:25 And we'll just have to rename the profile form back to the patient form.
00:09:31 And we'll have to properly export it by saying const patient form is equal to an arrow function.
00:09:38 And we're exporting it at the bottom.
00:09:40 So what we have done now is we have imported the Zad resolvers and use form from React Hook form, which allows us to define our form.
00:09:49 So we're defining our form right here at the top.
00:09:52 And we're saying that this form will be of a type, form schema, which right now contains only a username of minimum two characters.
00:10:02 And you can even provide additional messages for validation.
00:10:06 Next, we define an onSubmit handler where you render some logic that happens once the user submits the form.
00:10:14 And finally, we have the UI.
00:10:16 So going back right here, we can actually build our form using the form component.
00:10:22 So we have to copy all of the imports and paste them right at the top.
00:10:28 We have already installed a ShadCN button and a form, but now we have to also install an input.
00:10:34 So we can simply say MPX, ShadCN UI, latest add input.
00:10:40 And now that we have the imports, we can also just copy the actual form usage.
00:10:45 So let's take it and let's replace this div with the form that we just copied.
00:10:50 And of course we can properly indent it.
00:10:54 And that's it, we're done.
00:10:56 Our form should look something like this.
00:10:58 So let's check it out.
00:10:59 Hey, that looks great.
00:11:01 We have the username Shazian right here.
00:11:03 This is your public display name and we can actually type something and it looks good.
00:11:08 We also have a button to submit it and the button has complete validation.
00:11:12 So it says username must be at least two characters and it won't let us pass if we don't add at least two.
00:11:20 So there you have it.
00:11:21 This is your simple form, but now let's extend this even further.
00:11:26 Let's first do some basic styling by giving it a space dash y of six and a flex of one.
00:11:34 And within the form, let's also render an additional section that will have a class name equal to margin bottom of 12 and space y of four.
00:11:46 And here we can render an h1 that will simply say hi there.
00:11:51 And we can also render an emoji, maybe just a wave.
00:11:56 You can just copy this from Google.
00:11:58 And we can render a p tag there saying something like schedule your first appointment.
00:12:05 Let's style it a bit by giving this h1 a class name equal to header.
00:12:11 And we can also give this p tag a class name equal to text-dark-700.
00:12:18 Going back, this looks much better.
00:12:20 And after that, we immediately have our form field.
00:12:26 But now if you wanted to have more form fields, like in our application where we'll have hundreds of them, you would easily surpass couple hundred lines
00:12:37 of code, which is a lot.
00:12:39 So what you want to do in this case is copy this form field and turn it into a custom reusable form field component.
00:12:49 So let's do just that.
00:12:51 I have copied it and I will create a new component.
00:12:54 called custom form field.tsx.
00:13:00 And you can run RAFCE right here.
00:13:04 And instead of this div, we can paste our custom form field.
00:13:08 Let's also make sure to invent it properly.
00:13:12 And let's not forget to get all of the imports from the patient form into our new custom form field.
00:13:19 So I will leave just the form right here.
00:13:23 And all of the other ones will be moved to the custom form field right at the top.
00:13:28 Don't forget, this one will also have to be turned into a use client component.
00:13:36 And as you can see, we're also missing some additional things like the input, which we can also import right here from components input.
00:13:44 So let's just take it from here and paste it here.
00:13:47 And the next thing is this form.
00:13:49 And don't forget the form resides in the actual patient form component.
00:13:54 It is this form right here where we have defined it for the first time.
00:13:58 So now we have to figure out a way of how we can pass that form as a prop over to the custom form field.
00:14:05 Well, we can do that easily.
00:14:07 Let's go right here.
00:14:09 And let's render our custom form field imported from dot dot slash custom form field.
00:14:15 And to it, we can pass the control, which is equal to form dot control.
00:14:20 So now if we go back right here, we can accept a prop.
00:14:24 So that will be props of a type custom props, and we can define those custom props right here as an interface.
00:14:33 So interface custom props will have a control of a type control coming from react hook form of a type any.
00:14:42 And now we can replace the form control with just control coming from props, but of course we have to destructure it right here.
00:14:50 So let's destructure the control.
00:14:52 And as you can see, we're all good.
00:14:55 Now we still haven't made this a fully reusable component.
00:15:00 Do you know why?
00:15:01 Well, it's because this is just one single form field.
00:15:05 We could have just used it here as well, right?
00:15:08 The reusability happens once you allow this component to change and to morph to different use cases.
00:15:16 And how I approach this is you take a look at the things that should be the same for every single form input.
00:15:24 Like we have this form field and if you duplicate it two times, which are the lines which will be the same and which are the lines which will change?
00:15:34 Well, of course, the name will change as it will be different for every single input.
00:15:39 The label will change too.
00:15:41 Maybe even the type of the input will change and the description will surely change too.
00:15:47 So let's pass those as props.
00:15:50 First of all, let's define the field type.
00:15:53 And of course you could just say text or input, which is totally fine, but we can go a step further here and make it something known as an enum.
00:16:01 So let's scroll to the top of this component and let's say export enum.
00:16:08 form field types or just type is equal to an object.
00:16:15 And here we can say input is equal to a string of input.
00:16:20 So now we know that this form field type will be form field type dot input like this.
00:16:29 And if we go back into the custom form field, we can now accept the form field type, or we just call that field type, I believe,
00:16:38 and we can define it right here as field type is form field type coming from forms patient form.
00:16:51 So this now ensures that it can only be of one of the types we have specified above.
00:16:57 We could have just simply put a string of input right here, but with those types of strings, it's very easy to make a mistake and say something like int,
00:17:07 for example.
00:17:08 And then it will be very hard to notice a mistake here because if it were a string, it wouldn't throw an error.
00:17:14 But now it says, hey, input is not assignable to form field type.
00:17:18 Most likely it should have been an input.
00:17:22 So you just do it like this.
00:17:24 And then if you make a mistake here, it's quite obvious, right?
00:17:27 This doesn't exist and you get an immediate error.
00:17:31 Cool.
00:17:31 So now you learned about enums, which is also a very good thing to know about.
00:17:35 What else did we say we want to pass to this thing?
00:17:39 Well, let's pass a name equal to name.
00:17:42 Let's pass a label equal to full name.
00:17:46 Let's also pass a dynamic placeholder equal to John Doe.
00:17:51 Maybe some inputs will also have an icon source equal to forward slash assets, forward slash icons, forward slash user dot SVG.
00:18:00 And we can also pass an icon alt, which will be equal to user.
00:18:06 So all of these things will be different for every single input.
00:18:10 And now that we're passing them, we can just go into here, we can accept it, and then just use it.
00:18:16 Let me show you how.
00:18:17 The field type will be used to render different kinds of inputs or fields, but name is very simple.
00:18:23 So let's look into that first.
00:18:25 We can use the name and then instead of using a default username, which is always the same, now each one of our form fields can have a unique name.
00:18:35 And also we don't want every single one of our form fields to have the same layout.
00:18:41 This layout will be used for an input, but sometimes we maybe want to render something else.
00:18:46 So I will delete this entire thing and say form item.
00:18:53 Give it a class name equal to flex-1.
00:18:57 And here we can check if field type is not equal to form field type dot checkbox.
00:19:06 This is also one of the types we'll have in the future.
00:19:09 And if a label exists, then we want to render a form label.
00:19:16 That's going to look something like this.
00:19:17 And inside of there, we can render the actual label.
00:19:21 So now it's letting us know that the checkbox is not a valid type on their form field types.
00:19:26 So let's go ahead and add it.
00:19:28 We can have a text area, which will be equal to a string of text area.
00:19:34 We can also have a phone input, which will be phone underscore input is equal to phone.
00:19:40 Let's do phone input like this.
00:19:42 We can also have a checkbox equal to checkbox.
00:19:46 Later on, we'll have date pickers too.
00:19:49 So date picker is equal to date picker.
00:19:53 Select is equal to select.
00:19:56 And a skeleton input is equal to a skeleton.
00:20:01 So these are all the different types of form fields and make sure to add commas at the end of each one of these.
00:20:07 Great.
00:20:08 So now going back, it doesn't complain about the checkbox anymore.
00:20:12 And the label as we know is coming right from props.
00:20:16 So we can get it right here.
00:20:18 Also, our custom props are complaining about the fact that we're not accepting these types of props.
00:20:23 So let's define that as well.
00:20:25 Alongside the field type, we also have a name of a type string.
00:20:29 We have a label, which will be optional of a type string.
00:20:33 A placeholder, which will be optional of a type string.
00:20:37 An icon source, optional of a type string.
00:20:40 An icon alt, optional of a type string.
00:20:44 Disabled property.
00:20:46 Also optional of a type boolean, date format, also optional as it will only be shown in inputs containing a date.
00:20:55 So calendar inputs of a type string, show time select to further modify the calendar inputs, whether we want to show time or not of a type boolean.
00:21:07 Children, because sometimes we want to show something inside of an input of a type React.ReactNode, we also have a render skeleton,
00:21:17 which will be a function accepting a field and returning a React node.
00:21:23 This is typically used when we want to show a loading state for an input.
00:21:27 And I think that'll be enough for now.
00:21:31 So now it's not complaining about the props we're passing and it's getting the label right here.
00:21:38 And we're missing an end sign here as well.
00:21:41 So if the field type is not a checkbox and if a label exists, then show the label.
00:21:46 Next, we can create a new functional component that will render all different kinds of inputs.
00:21:53 const renderInput is equal to a functional component that returns those inputs.
00:22:01 For now, let's make it return a single input like this.
00:22:05 We can just pass the type text and the placeholder.
00:22:08 That's good enough for now.
00:22:10 And we can render this render input right here.
00:22:13 To it, we'll pass the field equal to field.
00:22:17 So we know what kind of field we want to render.
00:22:20 So maybe render field will be a better name.
00:22:23 Let's rename it render field because input is only one type of fields that there are.
00:22:29 And we can also pass all the other props we're passing into the custom form field by doing it like this.
00:22:38 So this means that we cannot destructure these properties right here at the top.
00:22:41 We have to leave the props exposed, but we can destructure them right here, const, and then get this out of the props.
00:22:50 This allows us to destructure them, but also pass the entire props object right here as well.
00:22:56 Now, we can also let TypeScript know what the render field will be accepting.
00:23:01 It'll be getting a field as well as props.
00:23:05 And this will be of a type.
00:23:07 Field is of a type any.
00:23:10 And props is of a type custom props.
00:23:15 There we go.
00:23:16 And then just at the bottom, we can render a form message to which we can just pass a class name equal to shad-error.
00:23:26 So you can see how we're making this component much more reusable.
00:23:30 It changes the name, it changes whether it shows the label or not, the form message, and also we can render different kinds of fields depending on the
00:23:40 form field type.
00:23:41 So going back to the code, you can see that we now have a full name and we still have an input.
00:23:46 So let's collapse this custom form field and let's actually focus on this render field because this is where we can customize this form field further.
00:23:56 Let me show you how.
00:23:57 Instead of always rendering an input, we will just return a switch case.
00:24:03 So we can say switch.
00:24:06 And switch typically takes a key.
00:24:09 It will be of a props dot field type and a value, which can be form field type.
00:24:19 dot input like this.
00:24:22 So only if the form field type is an input, then we can return something.
00:24:27 Let me show you what.
00:24:29 We can return a div that will have a class name equal to flex rounded dash MD border border-dark-500 and bgdark 400. So now we're creating the layout around
00:24:49 the actual input.
00:24:50 You can see how that will look like.
00:24:53 Next, only if props.iconSource exists.
00:24:57 Then we want to render the actual icon.
00:25:01 So we can render an image.
00:25:03 Of course, this will be a Next.js image.
00:25:05 We can give it a source equal to props dot icon source.
00:25:09 And just so we don't have to say props this and props that, we can destructure the props further right here by saying const and then destructure the field
00:25:20 type from props.
00:25:21 We can also destructure the icon source.
00:25:24 We can also get the icon.
00:25:25 Alt, we can get the placeholder.
00:25:28 I think there's many more things which we'll be able to get later on, especially as we dive into some more detailed form fields for dates and so on.
00:25:38 But this is good enough for now.
00:25:39 So now we can remove this props that field type.
00:25:42 It's just field type.
00:25:43 And also if an icon source exists, render an image with an icon source equal to this.
00:25:49 We can also give it a height equal to 24, a width equal to 24. An alt tag equal to icon alt or just icon if it doesn't exist.
00:26:03 And we can give it a class name equal to margin left of two.
00:26:08 Just to divide it a bit from the left.
00:26:09 Then below this icon source, we can render an actual form control inside of which we'll put a self-closing input tag.
00:26:20 With a placeholder equal to placeholder, we can destructure everything else from this field that we're passing into it and give it a class name equal to
00:26:32 shadInput and border-zero.
00:26:34 And this is our input.
00:26:36 So if we go back now, you can see how great this input looks like.
00:26:40 And since we have given it a name property, it automatically recognizes it and fills it using my password provider.
00:26:47 And I understand how this can seem like a lot of extra work that we're doing right now, but trust me, you'll appreciate it so much once you'll start creating
00:26:57 all of these inputs here, because you'll just be able to reuse all the great work you've done.
00:27:03 And instead of doing it in five or 600 lines of code, you'll be able to do it in just a couple.
00:27:09 So let's collapse this case of input.
00:27:11 And whenever we need to further modify this render field to add another type, we can easily do that by adding another case.
00:27:20 So let's see which other fields we need to add to our initial form.
00:27:25 That'll be an email address and a phone number.
00:27:29 Let's see how that works.
00:27:31 Going back to our patient form, the only thing we have to do right now is duplicate this custom form field and change some properties.
00:27:42 It will also be of a type input, name will be email, label will also be email.
00:27:49 Placeholder will be something like John Doe at jsmastery.pro.
00:27:55 We can pass a different icon such as email.svg and icon alt will be email.
00:28:03 And check this out.
00:28:05 I go back.
00:28:06 And without even reloading the page, a new input is here with a custom icon, placeholder, email type, and everything.
00:28:15 Looks great.
00:28:16 Now let's see how we can extend this to add a phone number field.
00:28:21 We'll duplicate this custom form field one more time, but we'll change the field type to phone underscore input.
00:28:29 To it, we can pass a name of phone.
00:28:34 and the label equal to phone number.
00:28:37 It won't have the icon source and alt and the placeholder will be something like 555-123-4567.
00:28:47 If we save it, you won't be able to see anything yet because our custom form field doesn't yet know how to render a phone input.
00:28:56 So let's extend it and let's make it happen.
00:29:00 I will add another case right here where the form field is of a type phone input.
00:29:07 And then we can return a new form control.
00:29:12 inside of which we'll have a phone input.
00:29:16 But of course, this phone input is not something we'll create on our own.
00:29:20 Typically, there's a saying that says, don't reinvent the wheel and we'll follow it.
00:29:26 So there's an amazing MPM package that has over half a million weekly downloads that does what we need it to do.
00:29:34 It's an international phone number input for React.
00:29:38 So let's just install it by running the command npm install react-phone-number-input-save.
00:29:48 And let's see how we can use it.
00:29:51 We simply need to import the styles and the phone input component, and then we use it like a regular input.
00:29:58 Let's give it a shot.
00:30:00 I will import these right at the top.
00:30:04 And I will try to call it.
00:30:05 It should work something like this, phone input, and it'll have a default country equal to US.
00:30:15 If we save it just like this, will it work?
00:30:17 Let's see, phone input.
00:30:20 I'm going to go back.
00:30:21 And yep, the phone input is here and you can actually choose from different countries in the world and it should pre-fill the country code.
00:30:29 This is looking great already.
00:30:31 Let's give it a placeholder equal to placeholder.
00:30:36 We can also give it the international flag with country calling code.
00:30:44 Value will be equal to field.value.
00:30:48 And just for TypeScript, we have to say as.
00:30:52 E164 number, so this is a special type or undefined in case it doesn't exist.
00:31:00 We also have to give it an onChange equal to field.onChange and we can give it a class name equal to input-phone.
00:31:13 Believe it or not, that's it for our phone input.
00:31:15 Check this out.
00:31:17 It looks amazing.
00:31:19 And if I reload the page, you can see that it adds the country code.
00:31:23 And if you change to a different country, it automatically prefills it.
00:31:27 But not only that, I can type my own country code and as soon as it recognizes it, it will also fill in the flag.
00:31:36 So we can do one or we can do nine one.
00:31:38 It all works great.
00:31:41 Or again, you can just choose it from the list right here and then continue typing the full number.
00:31:48 This is amazing.
00:31:51 And with that, we have our base form to add a patient.
00:31:56 We'll get back to this custom form field later on as we add more types of form fields.
00:32:01 But for now, let's go back to this patient form and let's figure out a way on how we can submit it.
00:32:08 Instead of having the regular submit button, we'll create a new custom component.
00:32:13 Let's call it SubmitButton.tsx and run RAFCE.
00:32:19 Immediately after, we can just call it right here, self-closing SubmitButton coming from dot dot slash SubmitButton.
00:32:29 And to it, we can pass the isLoading property, just so we know when to stop a button from submitting if it's already loading.
00:32:37 And we can dive into it and start implementing it.
00:32:40 Through the props, we can accept isLoading.
00:32:44 class name, if we're passing some different class names, and children, if you want to expand its looks.
00:32:51 And we can define that as button props.
00:32:53 We can define the types right here by declaring an interface of button props.
00:32:59 It'll have the isLoading property of boolean, class name, which will be optional of a type string, and children of a type React node.
00:33:10 And the only thing we have to do here is render an actual button.
00:33:15 So let's render a button coming from that slash UI button.
00:33:19 Let's give it a type equal to submit.
00:33:22 It will be disabled if it's currently loading.
00:33:25 So if is loading disabled, and we can give it a class name, which will look like this.
00:33:32 If we're passing some custom class names, then render that.
00:33:36 Else, render a shad-primary-btn and give it a W of full.
00:33:44 Next, if isLoading.
00:33:47 In that case, we want to show a div, which will show the loading state.
00:33:52 Else, we can simply return the children, meaning the button itself.
00:33:57 This div that will render the loading can have the class name equal to flex, items-center, and the gap of four.
00:34:05 And within it, we'll render a Next.js image.
00:34:10 that will have the source equal to forward slash assets, forward slash icons, forward slash loader dot SVG with an alt tag of loader,
00:34:20 a width of about 24, a height of about 24, two, and a class name equal to animate dash spin.
00:34:30 And below it, we can simply say loading dot dot dot.
00:34:34 Now let's see why this button is complaining.
00:34:36 It says button is not defined.
00:34:38 So we have to import it from UI button.
00:34:43 I think we are good now.
00:34:45 So if we go to the patient form, let's see if we're properly calling it.
00:34:49 Oh, it's expecting children.
00:34:51 So it's not self-closing.
00:34:53 Rather, we need to pass the text inside of it.
00:34:57 So we can say, get started.
00:34:59 Also, don't forget that this is loading is not defined.
00:35:03 So right at the top of our form, we can define a new use state.
00:35:10 Cold is loading, set is loading, and at the start, it'll be set to false.
00:35:17 Don't forget to import use state from React.
00:35:20 If we do this and go back, you can now see this beautiful get started button.
00:35:26 And with that, the UI for our auth form is done.
00:35:31 There is one thing that we have to fix though, and that is validation.
00:35:34 Right now, if you click get started, it won't really try to validate things.
00:35:39 And even if it did, it will only try to validate the username, which we don't technically have as our field.
00:35:45 So we have to make it have different values here in the default, such as name at the start empty string, email at the start empty string,
00:35:56 and phone at the start equal to an empty string as well.
00:35:59 But you can see that it's complaining, saying that these don't adhere to our form schema.
00:36:05 And typically it's a good idea to extract this form schema into a new file to keep it all tidy.
00:36:11 So let's copy this, take it away from here and go to our lib, within which we can create a new file called validation.ts.
00:36:23 There, you can paste this and don't forget to import Z from Zod, as that's what we use for validation.
00:36:30 Next, we can rename it as UserFormValidation.
00:36:35 Name will be a string with a minimum of two characters, and we can further extend this if you want to.
00:36:42 So minimum of two, if it's not two, then we can provide a message saying something like must be at least two characters.
00:36:51 We can then duplicate this.
00:36:53 We can do something like a max of 50. That's going to look like this.
00:36:58 And we can say name must be at most.
00:37:01 50 characters.
00:37:03 Next, we can also add a validation for email, z.string.email, invalid email address, and also for phone.
00:37:13 So phone will look like this, z.string.refine, and then here we can define how a phone input should look like.
00:37:22 And here we can create a regular expression that matches phone numbers.
00:37:28 So we can open up the regular expression and you can see that my GitHub Copilot automatically fills this out.
00:37:35 Pretty cool.
00:37:36 And if it doesn't autofill it for you, that's totally okay.
00:37:38 You can just pause this video and copy it out.
00:37:41 But most likely you're going to make some mistakes while copying it.
00:37:45 So I would highly recommend just going to the GitHub readme and copying this regular expression from there.
00:37:51 It should look something like this.
00:37:53 And finally, we can provide a message that's going to say something like invalid phone number.
00:37:59 Now that we have our validation, we can also export it by saying export const user form validation.
00:38:07 And instead of form schema, we can now exchange these three occurrences with user form validation, which we can import from lib validation.
00:38:17 And why is it complaining about a name?
00:38:20 It says that the name does not exist here.
00:38:22 Oh, I called it username.
00:38:24 It should have been name.
00:38:26 And now you can see it's good.
00:38:29 So now that we know that we are properly tracking everything, we have to focus on submitting this form.
00:38:36 So are we actually getting those values in there?
00:38:39 And once we are getting them, what are we going to do with them?
00:38:43 We want to create a new patient.
00:38:44 So at the start of the submit, we can set isLoading to be true because we're performing some kind of an asynchronous action.
00:38:53 So we also want to make this an async function.
00:38:57 Next, we're going to open up a try and catch block.
00:39:01 In the catch, we can simply console log the error if something goes wrong.
00:39:06 And in the try, we can form the user object.
00:39:10 We can destructure the values from the form.
00:39:13 That'll be name, email, and phone from the values right here.
00:39:16 So name, email, and phone.
00:39:19 And we can simply say name, email, and phone are the only things that this user will have coming from our form.
00:39:28 And this technically is not a real user because we haven't yet created it or passed this anywhere.
00:39:33 This is currently the data for the user.
00:39:35 So let me call it user data.
00:39:38 we have to take this user data and pass it somewhere to create this user in the database.
00:39:43 In this case, we'll be using appWrite, which means we'll have to call some kind of a function that looks like this.
00:39:50 await createUser to which we pass the user data.
00:39:56 And then that spits out a new user, which we can define into a user variable.
00:40:00 And finally, if.
00:40:02 we have that new user, we can use the router to push to the registration form.
00:40:07 So let's define that router above by saying const router is equal to use router coming from next navigation, not next router.
00:40:17 Make sure that it's coming from next navigation.
00:40:20 And if everything goes according to the plan, we can say router.push forward slash patients forward slash user dot dollar sign ID forward slash register.
00:40:32 And of course, this has to be a template string because this user ID will dynamically be coming from app rights database.
00:40:39 So for now, let's comment this part out because we haven't yet created the create user function, but it's so good to know that the entire front end part
00:40:49 of our form is completely done and it's waiting for us to connect it to the backend.
00:40:55 So let's do that next.