00:05
Next comes our account. So let's go ahead and get ourselves a controller set up for that. So node.ase.makeController. Again, plop that inside of our settings directory and call this our account controller.
00:15
We're going to want an index, an update email, and a destroy method inside of there. Today we're going to be specifically focusing on the update email. So we'll go ahead and make an action for that.
00:25
So node.ase.makeAction, put that inside of our settings directory and call that updateUserEmail. And for the validator, we will use that settings validator we created in the last lesson.
00:35
Let's start with our page. So within our settings directory, underneath our pages, let's create a new file and we'll call this account.view. We'll have a script set up with a lang of TS
00:45
and the template inside of here. We can start this off with our app head, title of account settings, description,
00:52
manage your plot my course account, and then wrap this in our settings shell. We're going to have two different forms on this page, one for our update email
01:02
and then one to actually delete our account. So to keep things clean, let's put those inside of separate components. So let's right click components, new file.
01:10
We'll call our first account email card.view. Again, let's get ourselves a setup script with a lang of TS and a template.
01:18
For our script, we're going to want to define props with an email of type string inside of it. And then we'll have our form,
01:26
which will use the form helper from Inertia and have our email set to an empty string and a password set to an empty string there as well.
01:34
For our template, we will again, use our card with a card header and a card title set to update email.
01:42
The card description can be update your accounts, email address, and then again, the card content will be our actual form. So we'll have our form.
01:52
Again, we'll add an ID of account email form. So we're going to have a button outside the form targeted. Give this a class of grid gap four, and then we'll have our form input.
02:02
View model set to form email. The type will be email. Any label will also be email. Give it an error of form errors email,
02:12
and then a placeholder of the email prop being provided in, which is going to be the user's current email address. I'm going to give it a save set of formats for me, and we can scroll on down because we also want to add in
02:22
a form input to have the user insert their account. Password. So we'll do form input. Self-close that view model equals form password.
02:31
The label for that will be confirm password. The type will be password placeholder of please confirm
02:39
your password to change your email and then the error of form errors password. So we'll have the user's current email come in as that
02:48
email prop and then we'll have them insert the new desired email into our form and also confirm that they are the actual account holder by inputting their password into
02:58
the form as well, where we'll confirm it before actually swapping the email. And by verifying that the user making the change knows the account password, we're making sure that it's not somebody
03:07
strolling along an unattended computer making this change on the user's behalf. And we also need to scroll on down now and add in our card order, which will contain our button. And let's give that a class again.
03:17
Border top PX6, PY4. And we can put our button in here with the form set to account email form with a type of submit at the loader
03:27
indicator from Lucid View next and show it when our form is processing. Give it a class of margin right to H4 with four and animate spin.
03:36
Give it a text of update email and this component should be a OK. So let's jump back into our account page and add it in. So account email card and give this an email prop which we'll
03:46
need to grab from the user. So in outside of our scripts, we can define props and we'll create our user, which will be a type user DTO that we can use
03:56
to pass the user email into that component. Let's jump back into the account email card real quick. We never added in our form handler. So let's do that while we're here at submit, prevent and do
04:06
form put to slash settings account email and then on success, we just need to call form reset there, which will
04:15
reset the form back to having an empty string for the email and password. All right. Let's give that a save and let's next rig up those routes. So let's jump into our web routes, give ourselves here a
04:24
new section called settings. Now we'll have a router dot get slash settings slash account that uses our accounts controller.
04:33
This will be for the index page as settings account index and we'll have our router dot put or our settings account email
04:42
that again uses our accounts controller to update the email as settings account email. And while we're here, we can go ahead and rig up the router
04:50
dot delete to our settings account, which will use the account controller destroy method as settings count destroy. All right.
04:59
Let's next jump into our settings validator and export const update email validator equals fine. Compile fine object.
05:08
We'll have our email field here, which we want to match the exact rule defined during our registration. So let's jump into our off and right here we have a register validator.
05:18
And let's go ahead and just select all of the emails rules here and give it a cut. Go ahead and export const new email rule and set that equal
05:27
to that bind rule that we've just cut. We can then apply this back to our register validator via new email rule dot loan.
05:35
Give that a safe set of formats and we can jump into our settings validator and apply it here too by calling new email rule, importing that from our off validator and then calling
05:45
clone here as well. You can move that out into a separate helper's file if you would wish, since that's mostly pertinent to authentication. I'm going to leave it inside of our off validator file.
05:54
In addition to our email, we also want our password validated. And here we're just going to be verifying that the password is correct for the email being provided.
06:04
So we just want to validate that it's a string and nothing further. Next, let's jump into our action. So we'll go up to settings, update user email where we're
06:12
going to want that user uptake user model and the data which we can infer from the type of our update email validator. Grab that out of our handle method.
06:22
And let's start by grabbing a reference to the user's old email and that's just going to be the user email property. Then we're going to want to verify that the password
06:30
provided is correct for the user's old email. So we'll await import our user model and call the verify
06:37
credentials method, providing the old email in as the UID and the data password in as the password. If the password is incorrect, this will throw an error for us.
06:47
Otherwise, it will allow us to continue onward for the actual changes inside of a database that we're going to be making. We'll want to wrap that inside of a transaction.
06:55
So let's get our managed transaction going here and let's tell the provided user model to use this transaction.
07:02
We can set the user's new email to the data email. We don't want to merge in here because we don't need to update the password at all. So we just set the email directly and we can call
07:12
await user save. And we also want to note this email change inside of our email histories table. So we'll await user related email histories and create a
07:22
new record inside of here with the email new set to the data email and the email old set to that email old property that we
07:31
grabbed at the start of this handle method. Then outside of our transaction, we can go ahead and return the updated user back. Now in the next lesson, we'll add an additional step to all
07:40
this that sends an email notification to the old user's email, notifying them that their email has been changed. For now, let's go ahead and jump into our account controller and fill this out.
07:50
So first we need for our index method to grab inertia and then return inertia, render our settings account page for the update email.
07:59
We want the request response session and off. Very first, what we'll do is grab the validated data. So we'll await request validate using updates,
08:09
email validator. Then we'll go ahead and grab the authenticated user using awaits off use web user and assert that because this route
08:18
will be protected by our off middleware. Our emails unique validation should prevent this. But just in case, let's do an additional check to ensure that
08:26
if the data email matches the user email, that the user actually knows that so we can session flash success.
08:34
You are already using the submitted email and then just go ahead and return response redirect on back again. The unique validation that we have should take care of that.
08:44
But just for sanity's sake there and then we'll await color update user email action called the handle method and provide the user and validated data in.
08:53
Then we go ahead and session flash success. Your email has been updated and then return response redirect on back. All right.
09:02
Now we should be able to jump back into our browser. Let's give that a refresh. There we go. Jump over to our account settings page must have named it something slightly different.
09:10
Let me come back into my web routes here. Oh, yep. Definitely got a typo there. Switch that to account. There we go. Come back into our browser.
09:18
OK, let's try jumping over into our account page one more time. There we go. OK, so now we have the email and password. Looks like the placeholders not quite there. Maybe I forgot to add it.
09:27
Let's just go back down to our account email card. Got a typo there too. I'm just check all those today. Placeholder. There we go. Jump back in here. Let's give it a refresh.
09:37
OK, maybe we never cascaded placeholder down through our form input. Let's check that real quick. Form input placeholder is a prop. Let's scroll down to the actual input here. There's our color.
09:47
Select input. Sure enough, placeholder equals placeholder. There we go. We're missing it on the actual text based input there. Scroll back over to here. There we go. Now we're seeing it.
09:57
So we have test user one as the user's current email being provided in as the placeholder. So let's try test user one. I can edit at test dot com. All right. Then we need to fill in the password.
10:07
So we'll do something there. Update email. And sure enough, your email has been updated and our placeholder has updated there accordingly.
10:14
So if we do test user one at test dot com and omit the password, we get password field must be defined, which is correct. Type something in there.
10:24
Update email. All right. That's good. And then if we provide in the user's current email, so test user one at test dot com, something in the password, update email. Yep. There we go. Email's already been taken.
10:34
So that's our validator clicking in there to show the user that message.