Skip to content

Commit ee17228

Browse files
committed
feat: add password reset email templates and notification handling
1 parent fc358cd commit ee17228

File tree

9 files changed

+102
-13
lines changed

9 files changed

+102
-13
lines changed

app/Enums/EmailCode.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ enum EmailCode: string
1111
case VERIFY_ACCOUNT = 'verify-account';
1212

1313
case ACCOUNT_VERIFICATION_SUCCESS = 'account-verification-success';
14+
15+
case PASSWORD_RESET_SUCCESS = 'password-reset-success';
16+
17+
case PASSWORD_RESET_LINK = 'password-reset-link';
1418
}

app/Listeners/SendEmailVerifiedMail.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace App\Listeners;
46

57
use App\Enums\EmailCode;
68
use App\Models\User;
79
use Illuminate\Auth\Events\Verified;
8-
use Illuminate\Contracts\Queue\ShouldQueue;
9-
use Illuminate\Queue\InteractsWithQueue;
1010

11-
class SendEmailVerifiedMail
11+
final class SendEmailVerifiedMail
1212
{
1313
/**
1414
* Create the event listener.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Listeners;
6+
7+
use App\Enums\EmailCode;
8+
use Illuminate\Auth\Events\PasswordReset;
9+
10+
final class SendPasswordUpdatedMail
11+
{
12+
/**
13+
* Create the event listener.
14+
*/
15+
public function __construct()
16+
{
17+
//
18+
}
19+
20+
/**
21+
* Handle the event.
22+
*/
23+
public function handle(PasswordReset $event): void
24+
{
25+
/** @var \App\Models\User $user */
26+
$user = $event->user;
27+
28+
$user->sendMail(template: EmailCode::PASSWORD_RESET_SUCCESS->value, replaceable: [
29+
'{name}' => $user->name,
30+
'{email}' => $user->email,
31+
'{login_link}' => route('login'),
32+
]);
33+
}
34+
}

app/Models/User.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ public function sendEmailVerificationNotification(): void
8383
]);
8484
}
8585

86+
public function sendPasswordResetNotification($token): void // @pest-ignore-type
87+
{
88+
$this->sendMail(template: EmailCode::PASSWORD_RESET_LINK->value, replaceable: [
89+
'{email}' => $this->email,
90+
'{password_reset_link}' => $this->resetUrl($token),
91+
]);
92+
}
93+
8694
private function verificationUrl(): string
8795
{
8896
/** @var int|float $ttl */
@@ -98,4 +106,12 @@ private function verificationUrl(): string
98106
]
99107
);
100108
}
109+
110+
private function resetUrl(string $token): string
111+
{
112+
return url(route('password.reset', [
113+
'token' => $token,
114+
'email' => $this->getEmailForPasswordReset(),
115+
], false));
116+
}
101117
}

app/Support/helpers.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
<?php
22

3+
declare(strict_types=1);
4+
5+
if (! function_exists('setting')) {
36

4-
if(!function_exists("setting")) {
5-
67
/**
78
* @param string|int|float|array<mixed>|bool|null $default
89
* @return string|int|float|array<mixed>|bool|null
910
*/
10-
function setting(string $option, mixed $default = null): string|int|float|array|bool|null{
11-
return app(App\Support\Facades\Setting::class)::get($option, $default);
11+
function setting(string $option, mixed $default = null): string|int|float|array|bool|null
12+
{
13+
return app(App\Support\Facades\Setting::class)::get($option, $default);
1214
}
13-
}
15+
}

app/Traits/MailableHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function sendMail(EmailTemplate|string|int $template, array $replaceable)
6060
* @var string $mailBody
6161
*/
6262
[$mailSubject, $mailBody] = $this->prepareMail($template, $replaceable);
63-
63+
6464
$toMail = $this instanceof Authenticatable ? $this->email : $this->emails;
6565

6666
// Send the mail to provided email

database/seeders/AppSettingSeeder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ public function run(): void
5757
]);
5858

5959
AppSetting::query()->firstOrCreate([
60-
'option' => 'mail_queue_enabled',
61-
'value' => 'false',
60+
'option' => 'mail_queue',
61+
'value' => 'true',
6262
]);
6363
}
6464
}

database/seeders/EmailTemplateSeeder.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,39 @@ public function run(): void
6161
6262
Congratulations your account verified successfully now you can login and continue.
6363
64+
[Login Now]({login_link})',
65+
]);
66+
67+
/**
68+
* Password reset request email
69+
*/
70+
EmailTemplate::query()->firstOrCreate([
71+
'subject' => 'Reset your pasword',
72+
'purpose' => 'Sent when user request password reset from forgot password',
73+
'code' => 'password-reset-link',
74+
'content' => "## Password reset request received
75+
We have received a request for password reset for the account associated with email: **{email}**.{br}
76+
77+
Please visit this link to reset your password: [Reset my password]({password_reset_link}){br}
78+
79+
OR{br}
80+
81+
[{password_reset_link}]({password_reset_link})
82+
83+
If you didn't make the request you can ignore this email.",
84+
]);
85+
86+
/**
87+
* Password reset success email template
88+
*/
89+
EmailTemplate::query()->firstOrCreate([
90+
'subject' => 'Your password has been reset',
91+
'purpose' => 'To be sent when password resets successfully',
92+
'code' => 'password-reset-success',
93+
'content' => '## Password updated successfully
94+
95+
We have updated your password as per your request. Now you can use your new password to login
96+
6497
[Login Now]({login_link})',
6598
]);
6699

resources/js/pages/auth/reset-password.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import NewPasswordController from '@/actions/App/Http/Controllers/Auth/NewPasswordController';
21
import { Form, Head } from '@inertiajs/react';
32
import { LoaderCircle } from 'lucide-react';
43

4+
import UserPasswordController from '@/actions/App/Http/Controllers/Auth/UserPasswordController';
55
import InputError from '@/components/input-error';
66
import { Button } from '@/components/ui/button';
77
import { Input } from '@/components/ui/input';
@@ -22,7 +22,7 @@ export default function ResetPassword({ token, email }: ResetPasswordProps) {
2222
<Head title="Reset password" />
2323

2424
<Form
25-
{...NewPasswordController.store.form()}
25+
action={UserPasswordController.store()}
2626
transform={(data) => ({ ...data, token, email })}
2727
resetOnSuccess={['password', 'password_confirmation']}
2828
>

0 commit comments

Comments
 (0)