Skip to content

Commit 6e114cd

Browse files
authored
[2.x] Verification improvements (#1048)
* wip * Add livewire side * Translations
1 parent 4b20263 commit 6e114cd

File tree

7 files changed

+144
-63
lines changed

7 files changed

+144
-63
lines changed

Diff for: routes/inertia.php

+27-25
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
? config('jetstream.auth_session')
2929
: null;
3030

31-
Route::group(['middleware' => array_values(array_filter([$authMiddleware, $authSessionMiddleware, 'verified']))], function () {
31+
Route::group(['middleware' => array_values(array_filter([$authMiddleware, $authSessionMiddleware]))], function () {
3232
// User & Profile...
3333
Route::get('/user/profile', [UserProfileController::class, 'show'])
3434
->name('profile.show');
@@ -44,32 +44,34 @@
4444
->name('current-user.destroy');
4545
}
4646

47-
// API...
48-
if (Jetstream::hasApiFeatures()) {
49-
Route::get('/user/api-tokens', [ApiTokenController::class, 'index'])->name('api-tokens.index');
50-
Route::post('/user/api-tokens', [ApiTokenController::class, 'store'])->name('api-tokens.store');
51-
Route::put('/user/api-tokens/{token}', [ApiTokenController::class, 'update'])->name('api-tokens.update');
52-
Route::delete('/user/api-tokens/{token}', [ApiTokenController::class, 'destroy'])->name('api-tokens.destroy');
53-
}
47+
Route::group(['middleware' => 'verified'], function () {
48+
// API...
49+
if (Jetstream::hasApiFeatures()) {
50+
Route::get('/user/api-tokens', [ApiTokenController::class, 'index'])->name('api-tokens.index');
51+
Route::post('/user/api-tokens', [ApiTokenController::class, 'store'])->name('api-tokens.store');
52+
Route::put('/user/api-tokens/{token}', [ApiTokenController::class, 'update'])->name('api-tokens.update');
53+
Route::delete('/user/api-tokens/{token}', [ApiTokenController::class, 'destroy'])->name('api-tokens.destroy');
54+
}
5455

55-
// Teams...
56-
if (Jetstream::hasTeamFeatures()) {
57-
Route::get('/teams/create', [TeamController::class, 'create'])->name('teams.create');
58-
Route::post('/teams', [TeamController::class, 'store'])->name('teams.store');
59-
Route::get('/teams/{team}', [TeamController::class, 'show'])->name('teams.show');
60-
Route::put('/teams/{team}', [TeamController::class, 'update'])->name('teams.update');
61-
Route::delete('/teams/{team}', [TeamController::class, 'destroy'])->name('teams.destroy');
62-
Route::put('/current-team', [CurrentTeamController::class, 'update'])->name('current-team.update');
63-
Route::post('/teams/{team}/members', [TeamMemberController::class, 'store'])->name('team-members.store');
64-
Route::put('/teams/{team}/members/{user}', [TeamMemberController::class, 'update'])->name('team-members.update');
65-
Route::delete('/teams/{team}/members/{user}', [TeamMemberController::class, 'destroy'])->name('team-members.destroy');
56+
// Teams...
57+
if (Jetstream::hasTeamFeatures()) {
58+
Route::get('/teams/create', [TeamController::class, 'create'])->name('teams.create');
59+
Route::post('/teams', [TeamController::class, 'store'])->name('teams.store');
60+
Route::get('/teams/{team}', [TeamController::class, 'show'])->name('teams.show');
61+
Route::put('/teams/{team}', [TeamController::class, 'update'])->name('teams.update');
62+
Route::delete('/teams/{team}', [TeamController::class, 'destroy'])->name('teams.destroy');
63+
Route::put('/current-team', [CurrentTeamController::class, 'update'])->name('current-team.update');
64+
Route::post('/teams/{team}/members', [TeamMemberController::class, 'store'])->name('team-members.store');
65+
Route::put('/teams/{team}/members/{user}', [TeamMemberController::class, 'update'])->name('team-members.update');
66+
Route::delete('/teams/{team}/members/{user}', [TeamMemberController::class, 'destroy'])->name('team-members.destroy');
6667

67-
Route::get('/team-invitations/{invitation}', [TeamInvitationController::class, 'accept'])
68-
->middleware(['signed'])
69-
->name('team-invitations.accept');
68+
Route::get('/team-invitations/{invitation}', [TeamInvitationController::class, 'accept'])
69+
->middleware(['signed'])
70+
->name('team-invitations.accept');
7071

71-
Route::delete('/team-invitations/{invitation}', [TeamInvitationController::class, 'destroy'])
72-
->name('team-invitations.destroy');
73-
}
72+
Route::delete('/team-invitations/{invitation}', [TeamInvitationController::class, 'destroy'])
73+
->name('team-invitations.destroy');
74+
}
75+
});
7476
});
7577
});

Diff for: routes/livewire.php

+20-19
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,26 @@
2424
? config('jetstream.auth_session')
2525
: null;
2626

27-
Route::group(['middleware' => array_values(array_filter([$authMiddleware, $authSessionMiddleware, 'verified']))], function () {
27+
Route::group(['middleware' => array_values(array_filter([$authMiddleware, $authSessionMiddleware]))], function () {
2828
// User & Profile...
29-
Route::get('/user/profile', [UserProfileController::class, 'show'])
30-
->name('profile.show');
31-
32-
// API...
33-
if (Jetstream::hasApiFeatures()) {
34-
Route::get('/user/api-tokens', [ApiTokenController::class, 'index'])->name('api-tokens.index');
35-
}
36-
37-
// Teams...
38-
if (Jetstream::hasTeamFeatures()) {
39-
Route::get('/teams/create', [TeamController::class, 'create'])->name('teams.create');
40-
Route::get('/teams/{team}', [TeamController::class, 'show'])->name('teams.show');
41-
Route::put('/current-team', [CurrentTeamController::class, 'update'])->name('current-team.update');
42-
43-
Route::get('/team-invitations/{invitation}', [TeamInvitationController::class, 'accept'])
44-
->middleware(['signed'])
45-
->name('team-invitations.accept');
46-
}
29+
Route::get('/user/profile', [UserProfileController::class, 'show'])->name('profile.show');
30+
31+
Route::group(['middleware' => 'verified'], function () {
32+
// API...
33+
if (Jetstream::hasApiFeatures()) {
34+
Route::get('/user/api-tokens', [ApiTokenController::class, 'index'])->name('api-tokens.index');
35+
}
36+
37+
// Teams...
38+
if (Jetstream::hasTeamFeatures()) {
39+
Route::get('/teams/create', [TeamController::class, 'create'])->name('teams.create');
40+
Route::get('/teams/{team}', [TeamController::class, 'show'])->name('teams.show');
41+
Route::put('/current-team', [CurrentTeamController::class, 'update'])->name('current-team.update');
42+
43+
Route::get('/team-invitations/{invitation}', [TeamInvitationController::class, 'accept'])
44+
->middleware(['signed'])
45+
->name('team-invitations.accept');
46+
}
47+
});
4748
});
4849
});

Diff for: src/Http/Livewire/UpdateProfileInformationForm.php

+19
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ class UpdateProfileInformationForm extends Component
2525
*/
2626
public $photo;
2727

28+
/**
29+
* Determine if the verification email was sent.
30+
*
31+
* @var bool
32+
*/
33+
public $verificationLinkSent = false;
34+
2835
/**
2936
* Prepare the component.
3037
*
@@ -73,6 +80,18 @@ public function deleteProfilePhoto()
7380
$this->emit('refresh-navigation-menu');
7481
}
7582

83+
/**
84+
* Sent the email verification.
85+
*
86+
* @return void
87+
*/
88+
public function sendEmailVerification()
89+
{
90+
Auth::user()->sendEmailVerificationNotification();
91+
92+
$this->verificationLinkSent = true;
93+
}
94+
7695
/**
7796
* Get the current user of the application.
7897
*

Diff for: stubs/inertia/resources/js/Pages/Auth/VerifyEmail.vue

+19-10
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ const verificationLinkSent = computed(() => props.status === 'verification-link-
2727
</template>
2828

2929
<div class="mb-4 text-sm text-gray-600">
30-
Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn't receive the email, we will gladly send you another.
30+
Before continuing, could you verify your email address by clicking on the link we just emailed to you? If you didn't receive the email, we will gladly send you another.
3131
</div>
3232

3333
<div v-if="verificationLinkSent" class="mb-4 font-medium text-sm text-green-600">
34-
A new verification link has been sent to the email address you provided during registration.
34+
A new verification link has been sent to the email address in your profile settings.
3535
</div>
3636

3737
<form @submit.prevent="submit">
@@ -40,14 +40,23 @@ const verificationLinkSent = computed(() => props.status === 'verification-link-
4040
Resend Verification Email
4141
</JetButton>
4242

43-
<Link
44-
:href="route('logout')"
45-
method="post"
46-
as="button"
47-
class="underline text-sm text-gray-600 hover:text-gray-900"
48-
>
49-
Log Out
50-
</Link>
43+
<div>
44+
<Link
45+
:href="route('profile.show')"
46+
class="underline text-sm text-gray-600 hover:text-gray-900"
47+
>
48+
Edit Profile
49+
</Link>
50+
51+
<Link
52+
:href="route('logout')"
53+
method="post"
54+
as="button"
55+
class="underline text-sm text-gray-600 hover:text-gray-900 ml-2"
56+
>
57+
Log Out
58+
</Link>
59+
</div>
5160
</div>
5261
</form>
5362
</JetAuthenticationCard>

Diff for: stubs/inertia/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup>
22
import { ref } from 'vue';
33
import { Inertia } from '@inertiajs/inertia';
4-
import { useForm } from '@inertiajs/inertia-vue3';
4+
import { Link, useForm } from '@inertiajs/inertia-vue3';
55
import JetButton from '@/Jetstream/Button.vue';
66
import JetFormSection from '@/Jetstream/FormSection.vue';
77
import JetInput from '@/Jetstream/Input.vue';
@@ -21,6 +21,7 @@ const form = useForm({
2121
photo: null,
2222
});
2323
24+
const verificationLinkSent = ref(null);
2425
const photoPreview = ref(null);
2526
const photoInput = ref(null);
2627
@@ -36,6 +37,10 @@ const updateProfileInformation = () => {
3637
});
3738
};
3839
40+
const sendEmailVerification = () => {
41+
verificationLinkSent.value = true;
42+
};
43+
3944
const selectNewPhoto = () => {
4045
photoInput.value.click();
4146
};
@@ -146,6 +151,26 @@ const clearPhotoFileInput = () => {
146151
class="mt-1 block w-full"
147152
/>
148153
<JetInputError :message="form.errors.email" class="mt-2" />
154+
155+
<div v-show="user.email_verified_at === null">
156+
<p class="text-sm mt-2">
157+
Your email address is unverified.
158+
159+
<Link
160+
:href="route('verification.send')"
161+
method="post"
162+
as="button"
163+
class="underline text-gray-600 hover:text-gray-900"
164+
@click.prevent="sendEmailVerification"
165+
>
166+
Click here to re-send the verification email.
167+
</Link>
168+
</p>
169+
170+
<div v-show="verificationLinkSent" class="mt-2 font-medium text-sm text-green-600">
171+
A new verification link has been sent to the above email address you.
172+
</div>
173+
</div>
149174
</div>
150175
</template>
151176

Diff for: stubs/livewire/resources/views/auth/verify-email.blade.php

+17-8
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
</x-slot>
66

77
<div class="mb-4 text-sm text-gray-600">
8-
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
8+
{{ __('Before continuing, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
99
</div>
1010

1111
@if (session('status') == 'verification-link-sent')
1212
<div class="mb-4 font-medium text-sm text-green-600">
13-
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
13+
{{ __('A new verification link has been sent to the email address in your profile settings.') }}
1414
</div>
1515
@endif
1616

@@ -25,13 +25,22 @@
2525
</div>
2626
</form>
2727

28-
<form method="POST" action="{{ route('logout') }}">
29-
@csrf
28+
<div>
29+
<a
30+
href="{{ route('profile.show') }}"
31+
class="underline text-sm text-gray-600 hover:text-gray-900"
32+
>
33+
{{ __('Edit Profile') }}
34+
</a>
3035

31-
<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900">
32-
{{ __('Log Out') }}
33-
</button>
34-
</form>
36+
<form method="POST" action="{{ route('logout') }}" class="inline">
37+
@csrf
38+
39+
<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900 ml-2">
40+
{{ __('Log Out') }}
41+
</button>
42+
</form>
43+
</div>
3544
</div>
3645
</x-jet-authentication-card>
3746
</x-guest-layout>

Diff for: stubs/livewire/resources/views/profile/update-profile-information-form.blade.php

+16
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@
6464
<x-jet-label for="email" value="{{ __('Email') }}" />
6565
<x-jet-input id="email" type="email" class="mt-1 block w-full" wire:model.defer="state.email" />
6666
<x-jet-input-error for="email" class="mt-2" />
67+
68+
@unless ($this->user->hasVerifiedEmail())
69+
<p class="text-sm mt-2">
70+
{{ __('Your email address is unverified.') }}
71+
72+
<button type="button" class="underline text-sm text-gray-600 hover:text-gray-900" wire:click.prevent="sendEmailVerification">
73+
{{ __('Click here to re-send the verification email.') }}
74+
</button>
75+
</p>
76+
77+
@if ($this->verificationLinkSent)
78+
<p v-show="verificationLinkSent" class="mt-2 font-medium text-sm text-green-600">
79+
{{ __('A new verification link has been sent to the above email address you.') }}
80+
</p>
81+
@endif
82+
@endif
6783
</div>
6884
</x-slot>
6985

0 commit comments

Comments
 (0)