├── LICENSE.md ├── README.md ├── composer.json ├── src ├── BreezeServiceProvider.php └── Console │ ├── InstallCommand.php │ ├── InstallsApiStack.php │ ├── InstallsBladeStack.php │ ├── InstallsInertiaStacks.php │ └── InstallsLivewireStack.php └── stubs ├── api ├── app │ ├── Http │ │ ├── Controllers │ │ │ └── Auth │ │ │ │ ├── AuthenticatedSessionController.php │ │ │ │ ├── EmailVerificationNotificationController.php │ │ │ │ ├── NewPasswordController.php │ │ │ │ ├── PasswordResetLinkController.php │ │ │ │ ├── RegisteredUserController.php │ │ │ │ └── VerifyEmailController.php │ │ ├── Middleware │ │ │ └── EnsureEmailIsVerified.php │ │ └── Requests │ │ │ └── Auth │ │ │ └── LoginRequest.php │ └── Providers │ │ └── AppServiceProvider.php ├── config │ ├── cors.php │ └── sanctum.php ├── pest-tests │ ├── Feature │ │ ├── Auth │ │ │ ├── AuthenticationTest.php │ │ │ ├── EmailVerificationTest.php │ │ │ ├── PasswordResetTest.php │ │ │ └── RegistrationTest.php │ │ └── ExampleTest.php │ ├── Pest.php │ └── Unit │ │ └── ExampleTest.php ├── routes │ ├── api.php │ ├── auth.php │ └── web.php └── tests │ └── Feature │ └── Auth │ ├── AuthenticationTest.php │ ├── EmailVerificationTest.php │ ├── PasswordResetTest.php │ └── RegistrationTest.php ├── default ├── app │ ├── Http │ │ ├── Controllers │ │ │ ├── Auth │ │ │ │ ├── AuthenticatedSessionController.php │ │ │ │ ├── ConfirmablePasswordController.php │ │ │ │ ├── EmailVerificationNotificationController.php │ │ │ │ ├── EmailVerificationPromptController.php │ │ │ │ ├── NewPasswordController.php │ │ │ │ ├── PasswordController.php │ │ │ │ ├── PasswordResetLinkController.php │ │ │ │ ├── RegisteredUserController.php │ │ │ │ └── VerifyEmailController.php │ │ │ └── ProfileController.php │ │ └── Requests │ │ │ ├── Auth │ │ │ └── LoginRequest.php │ │ │ └── ProfileUpdateRequest.php │ └── View │ │ └── Components │ │ ├── AppLayout.php │ │ └── GuestLayout.php ├── pest-tests │ ├── Feature │ │ ├── Auth │ │ │ ├── AuthenticationTest.php │ │ │ ├── EmailVerificationTest.php │ │ │ ├── PasswordConfirmationTest.php │ │ │ ├── PasswordResetTest.php │ │ │ ├── PasswordUpdateTest.php │ │ │ └── RegistrationTest.php │ │ ├── ExampleTest.php │ │ └── ProfileTest.php │ ├── Pest.php │ └── Unit │ │ └── ExampleTest.php ├── postcss.config.js ├── resources │ ├── css │ │ └── app.css │ ├── js │ │ └── app.js │ └── views │ │ ├── auth │ │ ├── confirm-password.blade.php │ │ ├── forgot-password.blade.php │ │ ├── login.blade.php │ │ ├── register.blade.php │ │ ├── reset-password.blade.php │ │ └── verify-email.blade.php │ │ ├── components │ │ ├── application-logo.blade.php │ │ ├── auth-session-status.blade.php │ │ ├── danger-button.blade.php │ │ ├── dropdown-link.blade.php │ │ ├── dropdown.blade.php │ │ ├── input-error.blade.php │ │ ├── input-label.blade.php │ │ ├── modal.blade.php │ │ ├── nav-link.blade.php │ │ ├── primary-button.blade.php │ │ ├── responsive-nav-link.blade.php │ │ ├── secondary-button.blade.php │ │ └── text-input.blade.php │ │ ├── dashboard.blade.php │ │ ├── layouts │ │ ├── app.blade.php │ │ ├── guest.blade.php │ │ └── navigation.blade.php │ │ └── profile │ │ ├── edit.blade.php │ │ └── partials │ │ ├── delete-user-form.blade.php │ │ ├── update-password-form.blade.php │ │ └── update-profile-information-form.blade.php ├── routes │ ├── auth.php │ └── web.php ├── tailwind.config.js ├── tests │ └── Feature │ │ ├── Auth │ │ ├── AuthenticationTest.php │ │ ├── EmailVerificationTest.php │ │ ├── PasswordConfirmationTest.php │ │ ├── PasswordResetTest.php │ │ ├── PasswordUpdateTest.php │ │ └── RegistrationTest.php │ │ └── ProfileTest.php └── vite.config.js ├── inertia-common ├── .prettierrc ├── app │ ├── Http │ │ ├── Controllers │ │ │ ├── Auth │ │ │ │ ├── AuthenticatedSessionController.php │ │ │ │ ├── ConfirmablePasswordController.php │ │ │ │ ├── EmailVerificationNotificationController.php │ │ │ │ ├── EmailVerificationPromptController.php │ │ │ │ ├── NewPasswordController.php │ │ │ │ ├── PasswordController.php │ │ │ │ ├── PasswordResetLinkController.php │ │ │ │ ├── RegisteredUserController.php │ │ │ │ └── VerifyEmailController.php │ │ │ └── ProfileController.php │ │ └── Middleware │ │ │ └── HandleInertiaRequests.php │ └── Providers │ │ └── AppServiceProvider.php ├── jsconfig.json ├── pest-tests │ └── Feature │ │ ├── Auth │ │ └── PasswordUpdateTest.php │ │ └── ProfileTest.php ├── routes │ ├── auth.php │ └── web.php ├── tailwind.config.js └── tests │ └── Feature │ ├── Auth │ └── PasswordUpdateTest.php │ └── ProfileTest.php ├── inertia-react-ts ├── .eslintrc.json ├── resources │ └── js │ │ ├── Components │ │ ├── ApplicationLogo.tsx │ │ ├── Checkbox.tsx │ │ ├── DangerButton.tsx │ │ ├── Dropdown.tsx │ │ ├── InputError.tsx │ │ ├── InputLabel.tsx │ │ ├── Modal.tsx │ │ ├── NavLink.tsx │ │ ├── PrimaryButton.tsx │ │ ├── ResponsiveNavLink.tsx │ │ ├── SecondaryButton.tsx │ │ └── TextInput.tsx │ │ ├── Layouts │ │ ├── AuthenticatedLayout.tsx │ │ └── GuestLayout.tsx │ │ ├── Pages │ │ ├── Auth │ │ │ ├── ConfirmPassword.tsx │ │ │ ├── ForgotPassword.tsx │ │ │ ├── Login.tsx │ │ │ ├── Register.tsx │ │ │ ├── ResetPassword.tsx │ │ │ └── VerifyEmail.tsx │ │ ├── Dashboard.tsx │ │ ├── Profile │ │ │ ├── Edit.tsx │ │ │ └── Partials │ │ │ │ ├── DeleteUserForm.tsx │ │ │ │ ├── UpdatePasswordForm.tsx │ │ │ │ └── UpdateProfileInformationForm.tsx │ │ └── Welcome.tsx │ │ ├── app.tsx │ │ ├── ssr.tsx │ │ └── types │ │ ├── global.d.ts │ │ ├── index.d.ts │ │ └── vite-env.d.ts └── tsconfig.json ├── inertia-react ├── .eslintrc.json ├── resources │ ├── js │ │ ├── Components │ │ │ ├── ApplicationLogo.jsx │ │ │ ├── Checkbox.jsx │ │ │ ├── DangerButton.jsx │ │ │ ├── Dropdown.jsx │ │ │ ├── InputError.jsx │ │ │ ├── InputLabel.jsx │ │ │ ├── Modal.jsx │ │ │ ├── NavLink.jsx │ │ │ ├── PrimaryButton.jsx │ │ │ ├── ResponsiveNavLink.jsx │ │ │ ├── SecondaryButton.jsx │ │ │ └── TextInput.jsx │ │ ├── Layouts │ │ │ ├── AuthenticatedLayout.jsx │ │ │ └── GuestLayout.jsx │ │ ├── Pages │ │ │ ├── Auth │ │ │ │ ├── ConfirmPassword.jsx │ │ │ │ ├── ForgotPassword.jsx │ │ │ │ ├── Login.jsx │ │ │ │ ├── Register.jsx │ │ │ │ ├── ResetPassword.jsx │ │ │ │ └── VerifyEmail.jsx │ │ │ ├── Dashboard.jsx │ │ │ ├── Profile │ │ │ │ ├── Edit.jsx │ │ │ │ └── Partials │ │ │ │ │ ├── DeleteUserForm.jsx │ │ │ │ │ ├── UpdatePasswordForm.jsx │ │ │ │ │ └── UpdateProfileInformationForm.jsx │ │ │ └── Welcome.jsx │ │ ├── app.jsx │ │ └── ssr.jsx │ └── views │ │ └── app.blade.php └── vite.config.js ├── inertia-vue-ts ├── .eslintrc.cjs ├── resources │ └── js │ │ ├── Components │ │ ├── ApplicationLogo.vue │ │ ├── Checkbox.vue │ │ ├── DangerButton.vue │ │ ├── Dropdown.vue │ │ ├── DropdownLink.vue │ │ ├── InputError.vue │ │ ├── InputLabel.vue │ │ ├── Modal.vue │ │ ├── NavLink.vue │ │ ├── PrimaryButton.vue │ │ ├── ResponsiveNavLink.vue │ │ ├── SecondaryButton.vue │ │ └── TextInput.vue │ │ ├── Layouts │ │ ├── AuthenticatedLayout.vue │ │ └── GuestLayout.vue │ │ ├── Pages │ │ ├── Auth │ │ │ ├── ConfirmPassword.vue │ │ │ ├── ForgotPassword.vue │ │ │ ├── Login.vue │ │ │ ├── Register.vue │ │ │ ├── ResetPassword.vue │ │ │ └── VerifyEmail.vue │ │ ├── Dashboard.vue │ │ ├── Profile │ │ │ ├── Edit.vue │ │ │ └── Partials │ │ │ │ ├── DeleteUserForm.vue │ │ │ │ ├── UpdatePasswordForm.vue │ │ │ │ └── UpdateProfileInformationForm.vue │ │ └── Welcome.vue │ │ ├── app.ts │ │ ├── ssr.ts │ │ └── types │ │ ├── global.d.ts │ │ ├── index.d.ts │ │ └── vite-env.d.ts └── tsconfig.json ├── inertia-vue ├── .eslintrc.cjs ├── resources │ ├── js │ │ ├── Components │ │ │ ├── ApplicationLogo.vue │ │ │ ├── Checkbox.vue │ │ │ ├── DangerButton.vue │ │ │ ├── Dropdown.vue │ │ │ ├── DropdownLink.vue │ │ │ ├── InputError.vue │ │ │ ├── InputLabel.vue │ │ │ ├── Modal.vue │ │ │ ├── NavLink.vue │ │ │ ├── PrimaryButton.vue │ │ │ ├── ResponsiveNavLink.vue │ │ │ ├── SecondaryButton.vue │ │ │ └── TextInput.vue │ │ ├── Layouts │ │ │ ├── AuthenticatedLayout.vue │ │ │ └── GuestLayout.vue │ │ ├── Pages │ │ │ ├── Auth │ │ │ │ ├── ConfirmPassword.vue │ │ │ │ ├── ForgotPassword.vue │ │ │ │ ├── Login.vue │ │ │ │ ├── Register.vue │ │ │ │ ├── ResetPassword.vue │ │ │ │ └── VerifyEmail.vue │ │ │ ├── Dashboard.vue │ │ │ ├── Profile │ │ │ │ ├── Edit.vue │ │ │ │ └── Partials │ │ │ │ │ ├── DeleteUserForm.vue │ │ │ │ │ ├── UpdatePasswordForm.vue │ │ │ │ │ └── UpdateProfileInformationForm.vue │ │ │ └── Welcome.vue │ │ ├── app.js │ │ └── ssr.js │ └── views │ │ └── app.blade.php └── vite.config.js ├── livewire-common ├── app │ └── Livewire │ │ ├── Actions │ │ └── Logout.php │ │ └── Forms │ │ └── LoginForm.php ├── pest-tests │ ├── Feature │ │ ├── Auth │ │ │ ├── AuthenticationTest.php │ │ │ ├── EmailVerificationTest.php │ │ │ ├── PasswordConfirmationTest.php │ │ │ ├── PasswordResetTest.php │ │ │ ├── PasswordUpdateTest.php │ │ │ └── RegistrationTest.php │ │ ├── ExampleTest.php │ │ └── ProfileTest.php │ ├── Pest.php │ └── Unit │ │ └── ExampleTest.php ├── resources │ └── views │ │ ├── components │ │ └── action-message.blade.php │ │ ├── dashboard.blade.php │ │ ├── layouts │ │ ├── app.blade.php │ │ └── guest.blade.php │ │ ├── profile.blade.php │ │ └── welcome.blade.php ├── routes │ ├── auth.php │ └── web.php └── tests │ └── Feature │ ├── Auth │ ├── AuthenticationTest.php │ ├── EmailVerificationTest.php │ ├── PasswordConfirmationTest.php │ ├── PasswordResetTest.php │ ├── PasswordUpdateTest.php │ └── RegistrationTest.php │ └── ProfileTest.php ├── livewire-functional └── resources │ └── views │ └── livewire │ ├── layout │ └── navigation.blade.php │ ├── pages │ └── auth │ │ ├── confirm-password.blade.php │ │ ├── forgot-password.blade.php │ │ ├── login.blade.php │ │ ├── register.blade.php │ │ ├── reset-password.blade.php │ │ └── verify-email.blade.php │ ├── profile │ ├── delete-user-form.blade.php │ ├── update-password-form.blade.php │ └── update-profile-information-form.blade.php │ └── welcome │ └── navigation.blade.php └── livewire └── resources └── views └── livewire ├── layout └── navigation.blade.php ├── pages └── auth │ ├── confirm-password.blade.php │ ├── forgot-password.blade.php │ ├── login.blade.php │ ├── register.blade.php │ ├── reset-password.blade.php │ └── verify-email.blade.php ├── profile ├── delete-user-form.blade.php ├── update-password-form.blade.php └── update-profile-information-form.blade.php └── welcome └── navigation.blade.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Taylor Otwell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This starter kit is for Laravel 11.x and prior. For our latest starter kits, check out: [https://laravel.com/starter-kits](https://laravel.com/starter-kits). 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/breeze", 3 | "description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.", 4 | "keywords": ["laravel", "auth"], 5 | "license": "MIT", 6 | "support": { 7 | "issues": "https://github.com/laravel/breeze/issues", 8 | "source": "https://github.com/laravel/breeze" 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Taylor Otwell", 13 | "email": "taylor@laravel.com" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.2.0", 18 | "illuminate/console": "^11.0|^12.0", 19 | "illuminate/filesystem": "^11.0|^12.0", 20 | "illuminate/support": "^11.0|^12.0", 21 | "illuminate/validation": "^11.0|^12.0", 22 | "symfony/console": "^7.0" 23 | }, 24 | "require-dev": { 25 | "laravel/framework": "^11.0|^12.0", 26 | "orchestra/testbench-core": "^9.0|^10.0", 27 | "phpstan/phpstan": "^2.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Laravel\\Breeze\\": "src/" 32 | } 33 | }, 34 | "extra": { 35 | "laravel": { 36 | "providers": [ 37 | "Laravel\\Breeze\\BreezeServiceProvider" 38 | ] 39 | } 40 | }, 41 | "config": { 42 | "sort-packages": true 43 | }, 44 | "minimum-stability": "dev", 45 | "prefer-stable": true 46 | } 47 | -------------------------------------------------------------------------------- /src/BreezeServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 28 | return; 29 | } 30 | 31 | $this->commands([ 32 | Console\InstallCommand::class, 33 | ]); 34 | } 35 | 36 | /** 37 | * Get the services provided by the provider. 38 | * 39 | * @return array 40 | */ 41 | public function provides() 42 | { 43 | return [Console\InstallCommand::class]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Controllers/Auth/AuthenticatedSessionController.php: -------------------------------------------------------------------------------- 1 | authenticate(); 19 | 20 | $request->session()->regenerate(); 21 | 22 | return response()->noContent(); 23 | } 24 | 25 | /** 26 | * Destroy an authenticated session. 27 | */ 28 | public function destroy(Request $request): Response 29 | { 30 | Auth::guard('web')->logout(); 31 | 32 | $request->session()->invalidate(); 33 | 34 | $request->session()->regenerateToken(); 35 | 36 | return response()->noContent(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended('/dashboard'); 19 | } 20 | 21 | $request->user()->sendEmailVerificationNotification(); 22 | 23 | return response()->json(['status' => 'verification-link-sent']); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Controllers/Auth/NewPasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 25 | 'token' => ['required'], 26 | 'email' => ['required', 'email'], 27 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 28 | ]); 29 | 30 | // Here we will attempt to reset the user's password. If it is successful we 31 | // will update the password on an actual user model and persist it to the 32 | // database. Otherwise we will parse the error and return the response. 33 | $status = Password::reset( 34 | $request->only('email', 'password', 'password_confirmation', 'token'), 35 | function ($user) use ($request) { 36 | $user->forceFill([ 37 | 'password' => Hash::make($request->string('password')), 38 | 'remember_token' => Str::random(60), 39 | ])->save(); 40 | 41 | event(new PasswordReset($user)); 42 | } 43 | ); 44 | 45 | if ($status != Password::PASSWORD_RESET) { 46 | throw ValidationException::withMessages([ 47 | 'email' => [__($status)], 48 | ]); 49 | } 50 | 51 | return response()->json(['status' => __($status)]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | validate([ 21 | 'email' => ['required', 'email'], 22 | ]); 23 | 24 | // We will send the password reset link to this user. Once we have attempted 25 | // to send the link, we will examine the response then see the message we 26 | // need to show to the user. Finally, we'll send out a proper response. 27 | $status = Password::sendResetLink( 28 | $request->only('email') 29 | ); 30 | 31 | if ($status != Password::RESET_LINK_SENT) { 32 | throw ValidationException::withMessages([ 33 | 'email' => [__($status)], 34 | ]); 35 | } 36 | 37 | return response()->json(['status' => __($status)]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Controllers/Auth/RegisteredUserController.php: -------------------------------------------------------------------------------- 1 | validate([ 24 | 'name' => ['required', 'string', 'max:255'], 25 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 26 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 27 | ]); 28 | 29 | $user = User::create([ 30 | 'name' => $request->name, 31 | 'email' => $request->email, 32 | 'password' => Hash::make($request->string('password')), 33 | ]); 34 | 35 | event(new Registered($user)); 36 | 37 | Auth::login($user); 38 | 39 | return response()->noContent(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended( 19 | config('app.frontend_url').'/dashboard?verified=1' 20 | ); 21 | } 22 | 23 | if ($request->user()->markEmailAsVerified()) { 24 | event(new Verified($request->user())); 25 | } 26 | 27 | return redirect()->intended( 28 | config('app.frontend_url').'/dashboard?verified=1' 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /stubs/api/app/Http/Middleware/EnsureEmailIsVerified.php: -------------------------------------------------------------------------------- 1 | user() || 20 | ($request->user() instanceof MustVerifyEmail && 21 | ! $request->user()->hasVerifiedEmail())) { 22 | return response()->json(['message' => 'Your email address is not verified.'], 409); 23 | } 24 | 25 | return $next($request); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stubs/api/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | getEmailForPasswordReset()}"; 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stubs/api/config/cors.php: -------------------------------------------------------------------------------- 1 | ['*'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => true, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | create(); 7 | 8 | $response = $this->post('/login', [ 9 | 'email' => $user->email, 10 | 'password' => 'password', 11 | ]); 12 | 13 | $this->assertAuthenticated(); 14 | $response->assertNoContent(); 15 | }); 16 | 17 | test('users can not authenticate with invalid password', function () { 18 | $user = User::factory()->create(); 19 | 20 | $this->post('/login', [ 21 | 'email' => $user->email, 22 | 'password' => 'wrong-password', 23 | ]); 24 | 25 | $this->assertGuest(); 26 | }); 27 | 28 | test('users can logout', function () { 29 | $user = User::factory()->create(); 30 | 31 | $response = $this->actingAs($user)->post('/logout'); 32 | 33 | $this->assertGuest(); 34 | $response->assertNoContent(); 35 | }); 36 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 10 | 11 | Event::fake(); 12 | 13 | $verificationUrl = URL::temporarySignedRoute( 14 | 'verification.verify', 15 | now()->addMinutes(60), 16 | ['id' => $user->id, 'hash' => sha1($user->email)] 17 | ); 18 | 19 | $response = $this->actingAs($user)->get($verificationUrl); 20 | 21 | Event::assertDispatched(Verified::class); 22 | expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); 23 | $response->assertRedirect(config('app.frontend_url').'/dashboard?verified=1'); 24 | }); 25 | 26 | test('email is not verified with invalid hash', function () { 27 | $user = User::factory()->unverified()->create(); 28 | 29 | $verificationUrl = URL::temporarySignedRoute( 30 | 'verification.verify', 31 | now()->addMinutes(60), 32 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 33 | ); 34 | 35 | $this->actingAs($user)->get($verificationUrl); 36 | 37 | expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); 38 | }); 39 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | create(); 11 | 12 | $this->post('/forgot-password', ['email' => $user->email]); 13 | 14 | Notification::assertSentTo($user, ResetPassword::class); 15 | }); 16 | 17 | test('password can be reset with valid token', function () { 18 | Notification::fake(); 19 | 20 | $user = User::factory()->create(); 21 | 22 | $this->post('/forgot-password', ['email' => $user->email]); 23 | 24 | Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) { 25 | $response = $this->post('/reset-password', [ 26 | 'token' => $notification->token, 27 | 'email' => $user->email, 28 | 'password' => 'password', 29 | 'password_confirmation' => 'password', 30 | ]); 31 | 32 | $response 33 | ->assertSessionHasNoErrors() 34 | ->assertStatus(200); 35 | 36 | return true; 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | post('/register', [ 5 | 'name' => 'Test User', 6 | 'email' => 'test@example.com', 7 | 'password' => 'password', 8 | 'password_confirmation' => 'password', 9 | ]); 10 | 11 | $this->assertAuthenticated(); 12 | $response->assertNoContent(); 13 | }); 14 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Pest.php: -------------------------------------------------------------------------------- 1 | extend(Tests\TestCase::class) 15 | ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) 16 | ->in('Feature'); 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Expectations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | When you're writing tests, you often need to check that values meet certain conditions. The 24 | | "expect()" function gives you access to a set of "expectations" methods that you can use 25 | | to assert different things. Of course, you may extend the Expectation API at any time. 26 | | 27 | */ 28 | 29 | expect()->extend('toBeOne', function () { 30 | return $this->toBe(1); 31 | }); 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Functions 36 | |-------------------------------------------------------------------------- 37 | | 38 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 39 | | project that you don't want to repeat in every file. Here you can also expose helpers as 40 | | global functions to help you to reduce the number of lines of code in your test files. 41 | | 42 | */ 43 | 44 | function something() 45 | { 46 | // .. 47 | } 48 | -------------------------------------------------------------------------------- /stubs/api/pest-tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /stubs/api/routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 7 | return $request->user(); 8 | }); 9 | -------------------------------------------------------------------------------- /stubs/api/routes/auth.php: -------------------------------------------------------------------------------- 1 | middleware('guest') 13 | ->name('register'); 14 | 15 | Route::post('/login', [AuthenticatedSessionController::class, 'store']) 16 | ->middleware('guest') 17 | ->name('login'); 18 | 19 | Route::post('/forgot-password', [PasswordResetLinkController::class, 'store']) 20 | ->middleware('guest') 21 | ->name('password.email'); 22 | 23 | Route::post('/reset-password', [NewPasswordController::class, 'store']) 24 | ->middleware('guest') 25 | ->name('password.store'); 26 | 27 | Route::get('/verify-email/{id}/{hash}', VerifyEmailController::class) 28 | ->middleware(['auth', 'signed', 'throttle:6,1']) 29 | ->name('verification.verify'); 30 | 31 | Route::post('/email/verification-notification', [EmailVerificationNotificationController::class, 'store']) 32 | ->middleware(['auth', 'throttle:6,1']) 33 | ->name('verification.send'); 34 | 35 | Route::post('/logout', [AuthenticatedSessionController::class, 'destroy']) 36 | ->middleware('auth') 37 | ->name('logout'); 38 | -------------------------------------------------------------------------------- /stubs/api/routes/web.php: -------------------------------------------------------------------------------- 1 | app()->version()]; 7 | }); 8 | 9 | require __DIR__.'/auth.php'; 10 | -------------------------------------------------------------------------------- /stubs/api/tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | $response = $this->post('/login', [ 18 | 'email' => $user->email, 19 | 'password' => 'password', 20 | ]); 21 | 22 | $this->assertAuthenticated(); 23 | $response->assertNoContent(); 24 | } 25 | 26 | public function test_users_can_not_authenticate_with_invalid_password(): void 27 | { 28 | $user = User::factory()->create(); 29 | 30 | $this->post('/login', [ 31 | 'email' => $user->email, 32 | 'password' => 'wrong-password', 33 | ]); 34 | 35 | $this->assertGuest(); 36 | } 37 | 38 | public function test_users_can_logout(): void 39 | { 40 | $user = User::factory()->create(); 41 | 42 | $response = $this->actingAs($user)->post('/logout'); 43 | 44 | $this->assertGuest(); 45 | $response->assertNoContent(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /stubs/api/tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 19 | 20 | Event::fake(); 21 | 22 | $verificationUrl = URL::temporarySignedRoute( 23 | 'verification.verify', 24 | now()->addMinutes(60), 25 | ['id' => $user->id, 'hash' => sha1($user->email)] 26 | ); 27 | 28 | $response = $this->actingAs($user)->get($verificationUrl); 29 | 30 | Event::assertDispatched(Verified::class); 31 | $this->assertTrue($user->fresh()->hasVerifiedEmail()); 32 | $response->assertRedirect(config('app.frontend_url').'/dashboard?verified=1'); 33 | } 34 | 35 | public function test_email_is_not_verified_with_invalid_hash(): void 36 | { 37 | $user = User::factory()->unverified()->create(); 38 | 39 | $verificationUrl = URL::temporarySignedRoute( 40 | 'verification.verify', 41 | now()->addMinutes(60), 42 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 43 | ); 44 | 45 | $this->actingAs($user)->get($verificationUrl); 46 | 47 | $this->assertFalse($user->fresh()->hasVerifiedEmail()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /stubs/api/tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | create(); 20 | 21 | $this->post('/forgot-password', ['email' => $user->email]); 22 | 23 | Notification::assertSentTo($user, ResetPassword::class); 24 | } 25 | 26 | public function test_password_can_be_reset_with_valid_token(): void 27 | { 28 | Notification::fake(); 29 | 30 | $user = User::factory()->create(); 31 | 32 | $this->post('/forgot-password', ['email' => $user->email]); 33 | 34 | Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) { 35 | $response = $this->post('/reset-password', [ 36 | 'token' => $notification->token, 37 | 'email' => $user->email, 38 | 'password' => 'password', 39 | 'password_confirmation' => 'password', 40 | ]); 41 | 42 | $response 43 | ->assertSessionHasNoErrors() 44 | ->assertStatus(200); 45 | 46 | return true; 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /stubs/api/tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | post('/register', [ 15 | 'name' => 'Test User', 16 | 'email' => 'test@example.com', 17 | 'password' => 'password', 18 | 'password_confirmation' => 'password', 19 | ]); 20 | 21 | $this->assertAuthenticated(); 22 | $response->assertNoContent(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/AuthenticatedSessionController.php: -------------------------------------------------------------------------------- 1 | authenticate(); 28 | 29 | $request->session()->regenerate(); 30 | 31 | return redirect()->intended(route('dashboard', absolute: false)); 32 | } 33 | 34 | /** 35 | * Destroy an authenticated session. 36 | */ 37 | public function destroy(Request $request): RedirectResponse 38 | { 39 | Auth::guard('web')->logout(); 40 | 41 | $request->session()->invalidate(); 42 | 43 | $request->session()->regenerateToken(); 44 | 45 | return redirect('/'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/ConfirmablePasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 28 | 'email' => $request->user()->email, 29 | 'password' => $request->password, 30 | ])) { 31 | throw ValidationException::withMessages([ 32 | 'password' => __('auth.password'), 33 | ]); 34 | } 35 | 36 | $request->session()->put('auth.password_confirmed_at', time()); 37 | 38 | return redirect()->intended(route('dashboard', absolute: false)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 17 | return redirect()->intended(route('dashboard', absolute: false)); 18 | } 19 | 20 | $request->user()->sendEmailVerificationNotification(); 21 | 22 | return back()->with('status', 'verification-link-sent'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/EmailVerificationPromptController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail() 18 | ? redirect()->intended(route('dashboard', absolute: false)) 19 | : view('auth.verify-email'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/PasswordController.php: -------------------------------------------------------------------------------- 1 | validateWithBag('updatePassword', [ 19 | 'current_password' => ['required', 'current_password'], 20 | 'password' => ['required', Password::defaults(), 'confirmed'], 21 | ]); 22 | 23 | $request->user()->update([ 24 | 'password' => Hash::make($validated['password']), 25 | ]); 26 | 27 | return back()->with('status', 'password-updated'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | validate([ 29 | 'email' => ['required', 'email'], 30 | ]); 31 | 32 | // We will send the password reset link to this user. Once we have attempted 33 | // to send the link, we will examine the response then see the message we 34 | // need to show to the user. Finally, we'll send out a proper response. 35 | $status = Password::sendResetLink( 36 | $request->only('email') 37 | ); 38 | 39 | return $status == Password::RESET_LINK_SENT 40 | ? back()->with('status', __($status)) 41 | : back()->withInput($request->only('email')) 42 | ->withErrors(['email' => __($status)]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/RegisteredUserController.php: -------------------------------------------------------------------------------- 1 | validate([ 33 | 'name' => ['required', 'string', 'max:255'], 34 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 35 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 36 | ]); 37 | 38 | $user = User::create([ 39 | 'name' => $request->name, 40 | 'email' => $request->email, 41 | 'password' => Hash::make($request->password), 42 | ]); 43 | 44 | event(new Registered($user)); 45 | 46 | Auth::login($user); 47 | 48 | return redirect(route('dashboard', absolute: false)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 19 | } 20 | 21 | if ($request->user()->markEmailAsVerified()) { 22 | event(new Verified($request->user())); 23 | } 24 | 25 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | $request->user(), 21 | ]); 22 | } 23 | 24 | /** 25 | * Update the user's profile information. 26 | */ 27 | public function update(ProfileUpdateRequest $request): RedirectResponse 28 | { 29 | $request->user()->fill($request->validated()); 30 | 31 | if ($request->user()->isDirty('email')) { 32 | $request->user()->email_verified_at = null; 33 | } 34 | 35 | $request->user()->save(); 36 | 37 | return Redirect::route('profile.edit')->with('status', 'profile-updated'); 38 | } 39 | 40 | /** 41 | * Delete the user's account. 42 | */ 43 | public function destroy(Request $request): RedirectResponse 44 | { 45 | $request->validateWithBag('userDeletion', [ 46 | 'password' => ['required', 'current_password'], 47 | ]); 48 | 49 | $user = $request->user(); 50 | 51 | Auth::logout(); 52 | 53 | $user->delete(); 54 | 55 | $request->session()->invalidate(); 56 | $request->session()->regenerateToken(); 57 | 58 | return Redirect::to('/'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /stubs/default/app/Http/Requests/ProfileUpdateRequest.php: -------------------------------------------------------------------------------- 1 | |string> 15 | */ 16 | public function rules(): array 17 | { 18 | return [ 19 | 'name' => ['required', 'string', 'max:255'], 20 | 'email' => [ 21 | 'required', 22 | 'string', 23 | 'lowercase', 24 | 'email', 25 | 'max:255', 26 | Rule::unique(User::class)->ignore($this->user()->id), 27 | ], 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /stubs/default/app/View/Components/AppLayout.php: -------------------------------------------------------------------------------- 1 | get('/login'); 7 | 8 | $response->assertStatus(200); 9 | }); 10 | 11 | test('users can authenticate using the login screen', function () { 12 | $user = User::factory()->create(); 13 | 14 | $response = $this->post('/login', [ 15 | 'email' => $user->email, 16 | 'password' => 'password', 17 | ]); 18 | 19 | $this->assertAuthenticated(); 20 | $response->assertRedirect(route('dashboard', absolute: false)); 21 | }); 22 | 23 | test('users can not authenticate with invalid password', function () { 24 | $user = User::factory()->create(); 25 | 26 | $this->post('/login', [ 27 | 'email' => $user->email, 28 | 'password' => 'wrong-password', 29 | ]); 30 | 31 | $this->assertGuest(); 32 | }); 33 | 34 | test('users can logout', function () { 35 | $user = User::factory()->create(); 36 | 37 | $response = $this->actingAs($user)->post('/logout'); 38 | 39 | $this->assertGuest(); 40 | $response->assertRedirect('/'); 41 | }); 42 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 10 | 11 | $response = $this->actingAs($user)->get('/verify-email'); 12 | 13 | $response->assertStatus(200); 14 | }); 15 | 16 | test('email can be verified', function () { 17 | $user = User::factory()->unverified()->create(); 18 | 19 | Event::fake(); 20 | 21 | $verificationUrl = URL::temporarySignedRoute( 22 | 'verification.verify', 23 | now()->addMinutes(60), 24 | ['id' => $user->id, 'hash' => sha1($user->email)] 25 | ); 26 | 27 | $response = $this->actingAs($user)->get($verificationUrl); 28 | 29 | Event::assertDispatched(Verified::class); 30 | expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); 31 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 32 | }); 33 | 34 | test('email is not verified with invalid hash', function () { 35 | $user = User::factory()->unverified()->create(); 36 | 37 | $verificationUrl = URL::temporarySignedRoute( 38 | 'verification.verify', 39 | now()->addMinutes(60), 40 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 41 | ); 42 | 43 | $this->actingAs($user)->get($verificationUrl); 44 | 45 | expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); 46 | }); 47 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 7 | 8 | $response = $this->actingAs($user)->get('/confirm-password'); 9 | 10 | $response->assertStatus(200); 11 | }); 12 | 13 | test('password can be confirmed', function () { 14 | $user = User::factory()->create(); 15 | 16 | $response = $this->actingAs($user)->post('/confirm-password', [ 17 | 'password' => 'password', 18 | ]); 19 | 20 | $response->assertRedirect(); 21 | $response->assertSessionHasNoErrors(); 22 | }); 23 | 24 | test('password is not confirmed with invalid password', function () { 25 | $user = User::factory()->create(); 26 | 27 | $response = $this->actingAs($user)->post('/confirm-password', [ 28 | 'password' => 'wrong-password', 29 | ]); 30 | 31 | $response->assertSessionHasErrors(); 32 | }); 33 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | get('/forgot-password'); 9 | 10 | $response->assertStatus(200); 11 | }); 12 | 13 | test('reset password link can be requested', function () { 14 | Notification::fake(); 15 | 16 | $user = User::factory()->create(); 17 | 18 | $this->post('/forgot-password', ['email' => $user->email]); 19 | 20 | Notification::assertSentTo($user, ResetPassword::class); 21 | }); 22 | 23 | test('reset password screen can be rendered', function () { 24 | Notification::fake(); 25 | 26 | $user = User::factory()->create(); 27 | 28 | $this->post('/forgot-password', ['email' => $user->email]); 29 | 30 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) { 31 | $response = $this->get('/reset-password/'.$notification->token); 32 | 33 | $response->assertStatus(200); 34 | 35 | return true; 36 | }); 37 | }); 38 | 39 | test('password can be reset with valid token', function () { 40 | Notification::fake(); 41 | 42 | $user = User::factory()->create(); 43 | 44 | $this->post('/forgot-password', ['email' => $user->email]); 45 | 46 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { 47 | $response = $this->post('/reset-password', [ 48 | 'token' => $notification->token, 49 | 'email' => $user->email, 50 | 'password' => 'password', 51 | 'password_confirmation' => 'password', 52 | ]); 53 | 54 | $response 55 | ->assertSessionHasNoErrors() 56 | ->assertRedirect(route('login')); 57 | 58 | return true; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $response = $this 10 | ->actingAs($user) 11 | ->from('/profile') 12 | ->put('/password', [ 13 | 'current_password' => 'password', 14 | 'password' => 'new-password', 15 | 'password_confirmation' => 'new-password', 16 | ]); 17 | 18 | $response 19 | ->assertSessionHasNoErrors() 20 | ->assertRedirect('/profile'); 21 | 22 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 23 | }); 24 | 25 | test('correct password must be provided to update password', function () { 26 | $user = User::factory()->create(); 27 | 28 | $response = $this 29 | ->actingAs($user) 30 | ->from('/profile') 31 | ->put('/password', [ 32 | 'current_password' => 'wrong-password', 33 | 'password' => 'new-password', 34 | 'password_confirmation' => 'new-password', 35 | ]); 36 | 37 | $response 38 | ->assertSessionHasErrorsIn('updatePassword', 'current_password') 39 | ->assertRedirect('/profile'); 40 | }); 41 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | 9 | test('new users can register', function () { 10 | $response = $this->post('/register', [ 11 | 'name' => 'Test User', 12 | 'email' => 'test@example.com', 13 | 'password' => 'password', 14 | 'password_confirmation' => 'password', 15 | ]); 16 | 17 | $this->assertAuthenticated(); 18 | $response->assertRedirect(route('dashboard', absolute: false)); 19 | }); 20 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Pest.php: -------------------------------------------------------------------------------- 1 | extend(Tests\TestCase::class) 15 | ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) 16 | ->in('Feature'); 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Expectations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | When you're writing tests, you often need to check that values meet certain conditions. The 24 | | "expect()" function gives you access to a set of "expectations" methods that you can use 25 | | to assert different things. Of course, you may extend the Expectation API at any time. 26 | | 27 | */ 28 | 29 | expect()->extend('toBeOne', function () { 30 | return $this->toBe(1); 31 | }); 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Functions 36 | |-------------------------------------------------------------------------- 37 | | 38 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 39 | | project that you don't want to repeat in every file. Here you can also expose helpers as 40 | | global functions to help you to reduce the number of lines of code in your test files. 41 | | 42 | */ 43 | 44 | function something() 45 | { 46 | // .. 47 | } 48 | -------------------------------------------------------------------------------- /stubs/default/pest-tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /stubs/default/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /stubs/default/resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /stubs/default/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | 3 | import Alpine from 'alpinejs'; 4 | 5 | window.Alpine = Alpine; 6 | 7 | Alpine.start(); 8 | -------------------------------------------------------------------------------- /stubs/default/resources/views/auth/confirm-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} 4 |
5 | 6 |
7 | @csrf 8 | 9 | 10 |
11 | 12 | 13 | 17 | 18 | 19 |
20 | 21 |
22 | 23 | {{ __('Confirm') }} 24 | 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /stubs/default/resources/views/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} 4 |
5 | 6 | 7 | 8 | 9 |
10 | @csrf 11 | 12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | {{ __('Email Password Reset Link') }} 22 | 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /stubs/default/resources/views/auth/reset-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | @csrf 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 | 26 | 29 | 30 | 31 |
32 | 33 |
34 | 35 | {{ __('Reset Password') }} 36 | 37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /stubs/default/resources/views/auth/verify-email.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ __('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.') }} 4 |
5 | 6 | @if (session('status') == 'verification-link-sent') 7 |
8 | {{ __('A new verification link has been sent to the email address you provided during registration.') }} 9 |
10 | @endif 11 | 12 |
13 |
14 | @csrf 15 | 16 |
17 | 18 | {{ __('Resend Verification Email') }} 19 | 20 |
21 |
22 | 23 |
24 | @csrf 25 | 26 | 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/auth-session-status.blade.php: -------------------------------------------------------------------------------- 1 | @props(['status']) 2 | 3 | @if ($status) 4 |
merge(['class' => 'font-medium text-sm text-green-600 dark:text-green-400']) }}> 5 | {{ $status }} 6 |
7 | @endif 8 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/danger-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/dropdown-link.blade.php: -------------------------------------------------------------------------------- 1 | merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out']) }}>{{ $slot }} 2 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/dropdown.blade.php: -------------------------------------------------------------------------------- 1 | @props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-700']) 2 | 3 | @php 4 | $alignmentClasses = match ($align) { 5 | 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', 6 | 'top' => 'origin-top', 7 | default => 'ltr:origin-top-right rtl:origin-top-left end-0', 8 | }; 9 | 10 | $width = match ($width) { 11 | '48' => 'w-48', 12 | default => $width, 13 | }; 14 | @endphp 15 | 16 |
17 |
18 | {{ $trigger }} 19 |
20 | 21 | 35 |
36 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/input-error.blade.php: -------------------------------------------------------------------------------- 1 | @props(['messages']) 2 | 3 | @if ($messages) 4 | 9 | @endif 10 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/input-label.blade.php: -------------------------------------------------------------------------------- 1 | @props(['value']) 2 | 3 | 6 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' 6 | : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/primary-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/responsive-nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-start text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out' 6 | : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 dark:hover:border-gray-600 focus:outline-none focus:text-gray-800 dark:focus:text-gray-200 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-600 transition duration-150 ease-in-out'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/secondary-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /stubs/default/resources/views/components/text-input.blade.php: -------------------------------------------------------------------------------- 1 | @props(['disabled' => false]) 2 | 3 | merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}> 4 | -------------------------------------------------------------------------------- /stubs/default/resources/views/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Dashboard') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | {{ __("You're logged in!") }} 13 |
14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /stubs/default/resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 |
19 | @include('layouts.navigation') 20 | 21 | 22 | @isset($header) 23 |
24 |
25 | {{ $header }} 26 |
27 |
28 | @endisset 29 | 30 | 31 |
32 | {{ $slot }} 33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /stubs/default/resources/views/layouts/guest.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 | 25 |
26 | {{ $slot }} 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /stubs/default/resources/views/profile/edit.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Profile') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | @include('profile.partials.update-profile-information-form') 13 |
14 |
15 | 16 |
17 |
18 | @include('profile.partials.update-password-form') 19 |
20 |
21 | 22 |
23 |
24 | @include('profile.partials.delete-user-form') 25 |
26 |
27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /stubs/default/routes/web.php: -------------------------------------------------------------------------------- 1 | middleware(['auth', 'verified'])->name('dashboard'); 13 | 14 | Route::middleware('auth')->group(function () { 15 | Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); 16 | Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); 17 | Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); 18 | }); 19 | 20 | require __DIR__.'/auth.php'; 21 | -------------------------------------------------------------------------------- /stubs/default/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from 'tailwindcss/defaultTheme'; 2 | import forms from '@tailwindcss/forms'; 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: [ 7 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', 8 | './storage/framework/views/*.php', 9 | './resources/views/**/*.blade.php', 10 | ], 11 | 12 | theme: { 13 | extend: { 14 | fontFamily: { 15 | sans: ['Figtree', ...defaultTheme.fontFamily.sans], 16 | }, 17 | }, 18 | }, 19 | 20 | plugins: [forms], 21 | }; 22 | -------------------------------------------------------------------------------- /stubs/default/tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | 20 | public function test_users_can_authenticate_using_the_login_screen(): void 21 | { 22 | $user = User::factory()->create(); 23 | 24 | $response = $this->post('/login', [ 25 | 'email' => $user->email, 26 | 'password' => 'password', 27 | ]); 28 | 29 | $this->assertAuthenticated(); 30 | $response->assertRedirect(route('dashboard', absolute: false)); 31 | } 32 | 33 | public function test_users_can_not_authenticate_with_invalid_password(): void 34 | { 35 | $user = User::factory()->create(); 36 | 37 | $this->post('/login', [ 38 | 'email' => $user->email, 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $this->assertGuest(); 43 | } 44 | 45 | public function test_users_can_logout(): void 46 | { 47 | $user = User::factory()->create(); 48 | 49 | $response = $this->actingAs($user)->post('/logout'); 50 | 51 | $this->assertGuest(); 52 | $response->assertRedirect('/'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /stubs/default/tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 19 | 20 | $response = $this->actingAs($user)->get('/verify-email'); 21 | 22 | $response->assertStatus(200); 23 | } 24 | 25 | public function test_email_can_be_verified(): void 26 | { 27 | $user = User::factory()->unverified()->create(); 28 | 29 | Event::fake(); 30 | 31 | $verificationUrl = URL::temporarySignedRoute( 32 | 'verification.verify', 33 | now()->addMinutes(60), 34 | ['id' => $user->id, 'hash' => sha1($user->email)] 35 | ); 36 | 37 | $response = $this->actingAs($user)->get($verificationUrl); 38 | 39 | Event::assertDispatched(Verified::class); 40 | $this->assertTrue($user->fresh()->hasVerifiedEmail()); 41 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 42 | } 43 | 44 | public function test_email_is_not_verified_with_invalid_hash(): void 45 | { 46 | $user = User::factory()->unverified()->create(); 47 | 48 | $verificationUrl = URL::temporarySignedRoute( 49 | 'verification.verify', 50 | now()->addMinutes(60), 51 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 52 | ); 53 | 54 | $this->actingAs($user)->get($verificationUrl); 55 | 56 | $this->assertFalse($user->fresh()->hasVerifiedEmail()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /stubs/default/tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | $response = $this->actingAs($user)->get('/confirm-password'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | 22 | public function test_password_can_be_confirmed(): void 23 | { 24 | $user = User::factory()->create(); 25 | 26 | $response = $this->actingAs($user)->post('/confirm-password', [ 27 | 'password' => 'password', 28 | ]); 29 | 30 | $response->assertRedirect(); 31 | $response->assertSessionHasNoErrors(); 32 | } 33 | 34 | public function test_password_is_not_confirmed_with_invalid_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this->actingAs($user)->post('/confirm-password', [ 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $response->assertSessionHasErrors(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /stubs/default/tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | $response = $this 19 | ->actingAs($user) 20 | ->from('/profile') 21 | ->put('/password', [ 22 | 'current_password' => 'password', 23 | 'password' => 'new-password', 24 | 'password_confirmation' => 'new-password', 25 | ]); 26 | 27 | $response 28 | ->assertSessionHasNoErrors() 29 | ->assertRedirect('/profile'); 30 | 31 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 32 | } 33 | 34 | public function test_correct_password_must_be_provided_to_update_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this 39 | ->actingAs($user) 40 | ->from('/profile') 41 | ->put('/password', [ 42 | 'current_password' => 'wrong-password', 43 | 'password' => 'new-password', 44 | 'password_confirmation' => 'new-password', 45 | ]); 46 | 47 | $response 48 | ->assertSessionHasErrorsIn('updatePassword', 'current_password') 49 | ->assertRedirect('/profile'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stubs/default/tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 15 | 16 | $response->assertStatus(200); 17 | } 18 | 19 | public function test_new_users_can_register(): void 20 | { 21 | $response = $this->post('/register', [ 22 | 'name' => 'Test User', 23 | 'email' => 'test@example.com', 24 | 'password' => 'password', 25 | 'password_confirmation' => 'password', 26 | ]); 27 | 28 | $this->assertAuthenticated(); 29 | $response->assertRedirect(route('dashboard', absolute: false)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /stubs/default/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: ['resources/css/app.css', 'resources/js/app.js'], 8 | refresh: true, 9 | }), 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /stubs/inertia-common/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "plugins": [ 4 | "prettier-plugin-organize-imports", 5 | "prettier-plugin-tailwindcss" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/AuthenticatedSessionController.php: -------------------------------------------------------------------------------- 1 | Route::has('password.request'), 23 | 'status' => session('status'), 24 | ]); 25 | } 26 | 27 | /** 28 | * Handle an incoming authentication request. 29 | */ 30 | public function store(LoginRequest $request): RedirectResponse 31 | { 32 | $request->authenticate(); 33 | 34 | $request->session()->regenerate(); 35 | 36 | return redirect()->intended(route('dashboard', absolute: false)); 37 | } 38 | 39 | /** 40 | * Destroy an authenticated session. 41 | */ 42 | public function destroy(Request $request): RedirectResponse 43 | { 44 | Auth::guard('web')->logout(); 45 | 46 | $request->session()->invalidate(); 47 | 48 | $request->session()->regenerateToken(); 49 | 50 | return redirect('/'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/ConfirmablePasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 29 | 'email' => $request->user()->email, 30 | 'password' => $request->password, 31 | ])) { 32 | throw ValidationException::withMessages([ 33 | 'password' => __('auth.password'), 34 | ]); 35 | } 36 | 37 | $request->session()->put('auth.password_confirmed_at', time()); 38 | 39 | return redirect()->intended(route('dashboard', absolute: false)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 17 | return redirect()->intended(route('dashboard', absolute: false)); 18 | } 19 | 20 | $request->user()->sendEmailVerificationNotification(); 21 | 22 | return back()->with('status', 'verification-link-sent'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/EmailVerificationPromptController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail() 19 | ? redirect()->intended(route('dashboard', absolute: false)) 20 | : Inertia::render('Auth/VerifyEmail', ['status' => session('status')]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/PasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 19 | 'current_password' => ['required', 'current_password'], 20 | 'password' => ['required', Password::defaults(), 'confirmed'], 21 | ]); 22 | 23 | $request->user()->update([ 24 | 'password' => Hash::make($validated['password']), 25 | ]); 26 | 27 | return back(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | session('status'), 22 | ]); 23 | } 24 | 25 | /** 26 | * Handle an incoming password reset link request. 27 | * 28 | * @throws \Illuminate\Validation\ValidationException 29 | */ 30 | public function store(Request $request): RedirectResponse 31 | { 32 | $request->validate([ 33 | 'email' => 'required|email', 34 | ]); 35 | 36 | // We will send the password reset link to this user. Once we have attempted 37 | // to send the link, we will examine the response then see the message we 38 | // need to show to the user. Finally, we'll send out a proper response. 39 | $status = Password::sendResetLink( 40 | $request->only('email') 41 | ); 42 | 43 | if ($status == Password::RESET_LINK_SENT) { 44 | return back()->with('status', __($status)); 45 | } 46 | 47 | throw ValidationException::withMessages([ 48 | 'email' => [trans($status)], 49 | ]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/RegisteredUserController.php: -------------------------------------------------------------------------------- 1 | validate([ 34 | 'name' => 'required|string|max:255', 35 | 'email' => 'required|string|lowercase|email|max:255|unique:'.User::class, 36 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 37 | ]); 38 | 39 | $user = User::create([ 40 | 'name' => $request->name, 41 | 'email' => $request->email, 42 | 'password' => Hash::make($request->password), 43 | ]); 44 | 45 | event(new Registered($user)); 46 | 47 | Auth::login($user); 48 | 49 | return redirect(route('dashboard', absolute: false)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 19 | } 20 | 21 | if ($request->user()->markEmailAsVerified()) { 22 | event(new Verified($request->user())); 23 | } 24 | 25 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | $request->user() instanceof MustVerifyEmail, 23 | 'status' => session('status'), 24 | ]); 25 | } 26 | 27 | /** 28 | * Update the user's profile information. 29 | */ 30 | public function update(ProfileUpdateRequest $request): RedirectResponse 31 | { 32 | $request->user()->fill($request->validated()); 33 | 34 | if ($request->user()->isDirty('email')) { 35 | $request->user()->email_verified_at = null; 36 | } 37 | 38 | $request->user()->save(); 39 | 40 | return Redirect::route('profile.edit'); 41 | } 42 | 43 | /** 44 | * Delete the user's account. 45 | */ 46 | public function destroy(Request $request): RedirectResponse 47 | { 48 | $request->validate([ 49 | 'password' => ['required', 'current_password'], 50 | ]); 51 | 52 | $user = $request->user(); 53 | 54 | Auth::logout(); 55 | 56 | $user->delete(); 57 | 58 | $request->session()->invalidate(); 59 | $request->session()->regenerateToken(); 60 | 61 | return Redirect::to('/'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Http/Middleware/HandleInertiaRequests.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function share(Request $request): array 31 | { 32 | return [ 33 | ...parent::share($request), 34 | 'auth' => [ 35 | 'user' => $request->user(), 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /stubs/inertia-common/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $response = $this 10 | ->actingAs($user) 11 | ->from('/profile') 12 | ->put('/password', [ 13 | 'current_password' => 'password', 14 | 'password' => 'new-password', 15 | 'password_confirmation' => 'new-password', 16 | ]); 17 | 18 | $response 19 | ->assertSessionHasNoErrors() 20 | ->assertRedirect('/profile'); 21 | 22 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 23 | }); 24 | 25 | test('correct password must be provided to update password', function () { 26 | $user = User::factory()->create(); 27 | 28 | $response = $this 29 | ->actingAs($user) 30 | ->from('/profile') 31 | ->put('/password', [ 32 | 'current_password' => 'wrong-password', 33 | 'password' => 'new-password', 34 | 'password_confirmation' => 'new-password', 35 | ]); 36 | 37 | $response 38 | ->assertSessionHasErrors('current_password') 39 | ->assertRedirect('/profile'); 40 | }); 41 | -------------------------------------------------------------------------------- /stubs/inertia-common/routes/web.php: -------------------------------------------------------------------------------- 1 | Route::has('login'), 11 | 'canRegister' => Route::has('register'), 12 | 'laravelVersion' => Application::VERSION, 13 | 'phpVersion' => PHP_VERSION, 14 | ]); 15 | }); 16 | 17 | Route::get('/dashboard', function () { 18 | return Inertia::render('Dashboard'); 19 | })->middleware(['auth', 'verified'])->name('dashboard'); 20 | 21 | Route::middleware('auth')->group(function () { 22 | Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); 23 | Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); 24 | Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); 25 | }); 26 | 27 | require __DIR__.'/auth.php'; 28 | -------------------------------------------------------------------------------- /stubs/inertia-common/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from 'tailwindcss/defaultTheme'; 2 | import forms from '@tailwindcss/forms'; 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: [ 7 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', 8 | './storage/framework/views/*.php', 9 | './resources/views/**/*.blade.php', 10 | './resources/js/**/*.vue', 11 | ], 12 | 13 | theme: { 14 | extend: { 15 | fontFamily: { 16 | sans: ['Figtree', ...defaultTheme.fontFamily.sans], 17 | }, 18 | }, 19 | }, 20 | 21 | plugins: [forms], 22 | }; 23 | -------------------------------------------------------------------------------- /stubs/inertia-common/tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | $response = $this 19 | ->actingAs($user) 20 | ->from('/profile') 21 | ->put('/password', [ 22 | 'current_password' => 'password', 23 | 'password' => 'new-password', 24 | 'password_confirmation' => 'new-password', 25 | ]); 26 | 27 | $response 28 | ->assertSessionHasNoErrors() 29 | ->assertRedirect('/profile'); 30 | 31 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 32 | } 33 | 34 | public function test_correct_password_must_be_provided_to_update_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this 39 | ->actingAs($user) 40 | ->from('/profile') 41 | ->put('/password', [ 42 | 'current_password' => 'wrong-password', 43 | 'password' => 'new-password', 44 | 'password_confirmation' => 'new-password', 45 | ]); 46 | 47 | $response 48 | ->assertSessionHasErrors('current_password') 49 | ->assertRedirect('/profile'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:react/recommended", 10 | "plugin:react-hooks/recommended", 11 | "plugin:prettier/recommended" 12 | ], 13 | "parser": "@typescript-eslint/parser", 14 | "parserOptions": { 15 | "ecmaVersion": "latest", 16 | "sourceType": "module" 17 | }, 18 | "rules": { 19 | "react/react-in-jsx-scope": "off", 20 | "react/prop-types": "off", 21 | "react/no-unescaped-entities": "off" 22 | }, 23 | "settings": { 24 | "react": { 25 | "version": "detect" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { InputHTMLAttributes } from 'react'; 2 | 3 | export default function Checkbox({ 4 | className = '', 5 | ...props 6 | }: InputHTMLAttributes) { 7 | return ( 8 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/DangerButton.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | 3 | export default function DangerButton({ 4 | className = '', 5 | disabled, 6 | children, 7 | ...props 8 | }: ButtonHTMLAttributes) { 9 | return ( 10 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/InputError.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from 'react'; 2 | 3 | export default function InputError({ 4 | message, 5 | className = '', 6 | ...props 7 | }: HTMLAttributes & { message?: string }) { 8 | return message ? ( 9 |

13 | {message} 14 |

15 | ) : null; 16 | } 17 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/InputLabel.tsx: -------------------------------------------------------------------------------- 1 | import { LabelHTMLAttributes } from 'react'; 2 | 3 | export default function InputLabel({ 4 | value, 5 | className = '', 6 | children, 7 | ...props 8 | }: LabelHTMLAttributes & { value?: string }) { 9 | return ( 10 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import { InertiaLinkProps, Link } from '@inertiajs/react'; 2 | 3 | export default function NavLink({ 4 | active = false, 5 | className = '', 6 | children, 7 | ...props 8 | }: InertiaLinkProps & { active: boolean }) { 9 | return ( 10 | 20 | {children} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/PrimaryButton.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | 3 | export default function PrimaryButton({ 4 | className = '', 5 | disabled, 6 | children, 7 | ...props 8 | }: ButtonHTMLAttributes) { 9 | return ( 10 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/ResponsiveNavLink.tsx: -------------------------------------------------------------------------------- 1 | import { InertiaLinkProps, Link } from '@inertiajs/react'; 2 | 3 | export default function ResponsiveNavLink({ 4 | active = false, 5 | className = '', 6 | children, 7 | ...props 8 | }: InertiaLinkProps & { active?: boolean }) { 9 | return ( 10 | 18 | {children} 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/SecondaryButton.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | 3 | export default function SecondaryButton({ 4 | type = 'button', 5 | className = '', 6 | disabled, 7 | children, 8 | ...props 9 | }: ButtonHTMLAttributes) { 10 | return ( 11 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Components/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | forwardRef, 3 | InputHTMLAttributes, 4 | useEffect, 5 | useImperativeHandle, 6 | useRef, 7 | } from 'react'; 8 | 9 | export default forwardRef(function TextInput( 10 | { 11 | type = 'text', 12 | className = '', 13 | isFocused = false, 14 | ...props 15 | }: InputHTMLAttributes & { isFocused?: boolean }, 16 | ref, 17 | ) { 18 | const localRef = useRef(null); 19 | 20 | useImperativeHandle(ref, () => ({ 21 | focus: () => localRef.current?.focus(), 22 | })); 23 | 24 | useEffect(() => { 25 | if (isFocused) { 26 | localRef.current?.focus(); 27 | } 28 | }, [isFocused]); 29 | 30 | return ( 31 | 40 | ); 41 | }); 42 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Layouts/GuestLayout.tsx: -------------------------------------------------------------------------------- 1 | import ApplicationLogo from '@/Components/ApplicationLogo'; 2 | import { Link } from '@inertiajs/react'; 3 | import { PropsWithChildren } from 'react'; 4 | 5 | export default function Guest({ children }: PropsWithChildren) { 6 | return ( 7 |
8 |
9 | 10 | 11 | 12 |
13 | 14 |
15 | {children} 16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Pages/Auth/ConfirmPassword.tsx: -------------------------------------------------------------------------------- 1 | import InputError from '@/Components/InputError'; 2 | import InputLabel from '@/Components/InputLabel'; 3 | import PrimaryButton from '@/Components/PrimaryButton'; 4 | import TextInput from '@/Components/TextInput'; 5 | import GuestLayout from '@/Layouts/GuestLayout'; 6 | import { Head, useForm } from '@inertiajs/react'; 7 | import { FormEventHandler } from 'react'; 8 | 9 | export default function ConfirmPassword() { 10 | const { data, setData, post, processing, errors, reset } = useForm({ 11 | password: '', 12 | }); 13 | 14 | const submit: FormEventHandler = (e) => { 15 | e.preventDefault(); 16 | 17 | post(route('password.confirm'), { 18 | onFinish: () => reset('password'), 19 | }); 20 | }; 21 | 22 | return ( 23 | 24 | 25 | 26 |
27 | This is a secure area of the application. Please confirm your 28 | password before continuing. 29 |
30 | 31 |
32 |
33 | 34 | 35 | setData('password', e.target.value)} 43 | /> 44 | 45 | 46 |
47 | 48 |
49 | 50 | Confirm 51 | 52 |
53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Pages/Auth/ForgotPassword.tsx: -------------------------------------------------------------------------------- 1 | import InputError from '@/Components/InputError'; 2 | import PrimaryButton from '@/Components/PrimaryButton'; 3 | import TextInput from '@/Components/TextInput'; 4 | import GuestLayout from '@/Layouts/GuestLayout'; 5 | import { Head, useForm } from '@inertiajs/react'; 6 | import { FormEventHandler } from 'react'; 7 | 8 | export default function ForgotPassword({ status }: { status?: string }) { 9 | const { data, setData, post, processing, errors } = useForm({ 10 | email: '', 11 | }); 12 | 13 | const submit: FormEventHandler = (e) => { 14 | e.preventDefault(); 15 | 16 | post(route('password.email')); 17 | }; 18 | 19 | return ( 20 | 21 | 22 | 23 |
24 | Forgot your password? No problem. Just let us know your email 25 | address and we will email you a password reset link that will 26 | allow you to choose a new one. 27 |
28 | 29 | {status && ( 30 |
31 | {status} 32 |
33 | )} 34 | 35 |
36 | setData('email', e.target.value)} 44 | /> 45 | 46 | 47 | 48 |
49 | 50 | Email Password Reset Link 51 | 52 |
53 | 54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; 2 | import { Head } from '@inertiajs/react'; 3 | 4 | export default function Dashboard() { 5 | return ( 6 | 9 | Dashboard 10 | 11 | } 12 | > 13 | 14 | 15 |
16 |
17 |
18 |
19 | You're logged in! 20 |
21 |
22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/Pages/Profile/Edit.tsx: -------------------------------------------------------------------------------- 1 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; 2 | import { PageProps } from '@/types'; 3 | import { Head } from '@inertiajs/react'; 4 | import DeleteUserForm from './Partials/DeleteUserForm'; 5 | import UpdatePasswordForm from './Partials/UpdatePasswordForm'; 6 | import UpdateProfileInformationForm from './Partials/UpdateProfileInformationForm'; 7 | 8 | export default function Edit({ 9 | mustVerifyEmail, 10 | status, 11 | }: PageProps<{ mustVerifyEmail: boolean; status?: string }>) { 12 | return ( 13 | 16 | Profile 17 | 18 | } 19 | > 20 | 21 | 22 |
23 |
24 |
25 | 30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/app.tsx: -------------------------------------------------------------------------------- 1 | import '../css/app.css'; 2 | import './bootstrap'; 3 | 4 | import { createInertiaApp } from '@inertiajs/react'; 5 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 6 | import { createRoot } from 'react-dom/client'; 7 | 8 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 9 | 10 | createInertiaApp({ 11 | title: (title) => `${title} - ${appName}`, 12 | resolve: (name) => 13 | resolvePageComponent( 14 | `./Pages/${name}.tsx`, 15 | import.meta.glob('./Pages/**/*.tsx'), 16 | ), 17 | setup({ el, App, props }) { 18 | const root = createRoot(el); 19 | 20 | root.render(); 21 | }, 22 | progress: { 23 | color: '#4B5563', 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/ssr.tsx: -------------------------------------------------------------------------------- 1 | import { createInertiaApp } from '@inertiajs/react'; 2 | import createServer from '@inertiajs/react/server'; 3 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 4 | import ReactDOMServer from 'react-dom/server'; 5 | import { RouteName } from 'ziggy-js'; 6 | import { route } from '../../vendor/tightenco/ziggy'; 7 | 8 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 9 | 10 | createServer((page) => 11 | createInertiaApp({ 12 | page, 13 | render: ReactDOMServer.renderToString, 14 | title: (title) => `${title} - ${appName}`, 15 | resolve: (name) => 16 | resolvePageComponent( 17 | `./Pages/${name}.tsx`, 18 | import.meta.glob('./Pages/**/*.tsx'), 19 | ), 20 | setup: ({ App, props }) => { 21 | /* eslint-disable */ 22 | // @ts-expect-error 23 | global.route = (name, params, absolute) => 24 | route(name, params as any, absolute, { 25 | ...page.props.ziggy, 26 | location: new URL(page.props.ziggy.location), 27 | }); 28 | /* eslint-enable */ 29 | 30 | return ; 31 | }, 32 | }), 33 | ); 34 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import { PageProps as InertiaPageProps } from '@inertiajs/core'; 2 | import { AxiosInstance } from 'axios'; 3 | import { route as ziggyRoute } from 'ziggy-js'; 4 | import { PageProps as AppPageProps } from './'; 5 | 6 | declare global { 7 | interface Window { 8 | axios: AxiosInstance; 9 | } 10 | 11 | /* eslint-disable no-var */ 12 | var route: typeof ziggyRoute; 13 | } 14 | 15 | declare module '@inertiajs/core' { 16 | interface PageProps extends InertiaPageProps, AppPageProps {} 17 | } 18 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | name: string; 4 | email: string; 5 | email_verified_at?: string; 6 | } 7 | 8 | export type PageProps< 9 | T extends Record = Record, 10 | > = T & { 11 | auth: { 12 | user: User; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/resources/js/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /stubs/inertia-react-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "jsx": "react-jsx", 7 | "strict": true, 8 | "isolatedModules": true, 9 | "target": "ESNext", 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "skipLibCheck": true, 13 | "noEmit": true, 14 | "paths": { 15 | "@/*": ["./resources/js/*"], 16 | "ziggy-js": ["./vendor/tightenco/ziggy"] 17 | } 18 | }, 19 | "include": ["resources/js/**/*.ts", "resources/js/**/*.tsx", "resources/js/**/*.d.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /stubs/inertia-react/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:react-hooks/recommended", 10 | "plugin:prettier/recommended" 11 | ], 12 | "parserOptions": { 13 | "ecmaVersion": "latest", 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "react/react-in-jsx-scope": "off", 18 | "react/prop-types": "off", 19 | "react/no-unescaped-entities": "off", 20 | "no-undef": "off" 21 | }, 22 | "settings": { 23 | "react": { 24 | "version": "detect" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/Checkbox.jsx: -------------------------------------------------------------------------------- 1 | export default function Checkbox({ className = '', ...props }) { 2 | return ( 3 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/DangerButton.jsx: -------------------------------------------------------------------------------- 1 | export default function DangerButton({ 2 | className = '', 3 | disabled, 4 | children, 5 | ...props 6 | }) { 7 | return ( 8 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/InputError.jsx: -------------------------------------------------------------------------------- 1 | export default function InputError({ message, className = '', ...props }) { 2 | return message ? ( 3 |

7 | {message} 8 |

9 | ) : null; 10 | } 11 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/InputLabel.jsx: -------------------------------------------------------------------------------- 1 | export default function InputLabel({ 2 | value, 3 | className = '', 4 | children, 5 | ...props 6 | }) { 7 | return ( 8 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/NavLink.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from '@inertiajs/react'; 2 | 3 | export default function NavLink({ 4 | active = false, 5 | className = '', 6 | children, 7 | ...props 8 | }) { 9 | return ( 10 | 20 | {children} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/PrimaryButton.jsx: -------------------------------------------------------------------------------- 1 | export default function PrimaryButton({ 2 | className = '', 3 | disabled, 4 | children, 5 | ...props 6 | }) { 7 | return ( 8 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/ResponsiveNavLink.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from '@inertiajs/react'; 2 | 3 | export default function ResponsiveNavLink({ 4 | active = false, 5 | className = '', 6 | children, 7 | ...props 8 | }) { 9 | return ( 10 | 18 | {children} 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/SecondaryButton.jsx: -------------------------------------------------------------------------------- 1 | export default function SecondaryButton({ 2 | type = 'button', 3 | className = '', 4 | disabled, 5 | children, 6 | ...props 7 | }) { 8 | return ( 9 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Components/TextInput.jsx: -------------------------------------------------------------------------------- 1 | import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; 2 | 3 | export default forwardRef(function TextInput( 4 | { type = 'text', className = '', isFocused = false, ...props }, 5 | ref, 6 | ) { 7 | const localRef = useRef(null); 8 | 9 | useImperativeHandle(ref, () => ({ 10 | focus: () => localRef.current?.focus(), 11 | })); 12 | 13 | useEffect(() => { 14 | if (isFocused) { 15 | localRef.current?.focus(); 16 | } 17 | }, [isFocused]); 18 | 19 | return ( 20 | 29 | ); 30 | }); 31 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Layouts/GuestLayout.jsx: -------------------------------------------------------------------------------- 1 | import ApplicationLogo from '@/Components/ApplicationLogo'; 2 | import { Link } from '@inertiajs/react'; 3 | 4 | export default function GuestLayout({ children }) { 5 | return ( 6 |
7 |
8 | 9 | 10 | 11 |
12 | 13 |
14 | {children} 15 |
16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Pages/Auth/ConfirmPassword.jsx: -------------------------------------------------------------------------------- 1 | import InputError from '@/Components/InputError'; 2 | import InputLabel from '@/Components/InputLabel'; 3 | import PrimaryButton from '@/Components/PrimaryButton'; 4 | import TextInput from '@/Components/TextInput'; 5 | import GuestLayout from '@/Layouts/GuestLayout'; 6 | import { Head, useForm } from '@inertiajs/react'; 7 | 8 | export default function ConfirmPassword() { 9 | const { data, setData, post, processing, errors, reset } = useForm({ 10 | password: '', 11 | }); 12 | 13 | const submit = (e) => { 14 | e.preventDefault(); 15 | 16 | post(route('password.confirm'), { 17 | onFinish: () => reset('password'), 18 | }); 19 | }; 20 | 21 | return ( 22 | 23 | 24 | 25 |
26 | This is a secure area of the application. Please confirm your 27 | password before continuing. 28 |
29 | 30 |
31 |
32 | 33 | 34 | setData('password', e.target.value)} 42 | /> 43 | 44 | 45 |
46 | 47 |
48 | 49 | Confirm 50 | 51 |
52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Pages/Auth/ForgotPassword.jsx: -------------------------------------------------------------------------------- 1 | import InputError from '@/Components/InputError'; 2 | import PrimaryButton from '@/Components/PrimaryButton'; 3 | import TextInput from '@/Components/TextInput'; 4 | import GuestLayout from '@/Layouts/GuestLayout'; 5 | import { Head, useForm } from '@inertiajs/react'; 6 | 7 | export default function ForgotPassword({ status }) { 8 | const { data, setData, post, processing, errors } = useForm({ 9 | email: '', 10 | }); 11 | 12 | const submit = (e) => { 13 | e.preventDefault(); 14 | 15 | post(route('password.email')); 16 | }; 17 | 18 | return ( 19 | 20 | 21 | 22 |
23 | Forgot your password? No problem. Just let us know your email 24 | address and we will email you a password reset link that will 25 | allow you to choose a new one. 26 |
27 | 28 | {status && ( 29 |
30 | {status} 31 |
32 | )} 33 | 34 |
35 | setData('email', e.target.value)} 43 | /> 44 | 45 | 46 | 47 |
48 | 49 | Email Password Reset Link 50 | 51 |
52 | 53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Pages/Auth/VerifyEmail.jsx: -------------------------------------------------------------------------------- 1 | import PrimaryButton from '@/Components/PrimaryButton'; 2 | import GuestLayout from '@/Layouts/GuestLayout'; 3 | import { Head, Link, useForm } from '@inertiajs/react'; 4 | 5 | export default function VerifyEmail({ status }) { 6 | const { post, processing } = useForm({}); 7 | 8 | const submit = (e) => { 9 | e.preventDefault(); 10 | 11 | post(route('verification.send')); 12 | }; 13 | 14 | return ( 15 | 16 | 17 | 18 |
19 | Thanks for signing up! Before getting started, could you verify 20 | your email address by clicking on the link we just emailed to 21 | you? If you didn't receive the email, we will gladly send you 22 | another. 23 |
24 | 25 | {status === 'verification-link-sent' && ( 26 |
27 | A new verification link has been sent to the email address 28 | you provided during registration. 29 |
30 | )} 31 | 32 |
33 |
34 | 35 | Resend Verification Email 36 | 37 | 38 | 44 | Log Out 45 | 46 |
47 |
48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Pages/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; 2 | import { Head } from '@inertiajs/react'; 3 | 4 | export default function Dashboard() { 5 | return ( 6 | 9 | Dashboard 10 | 11 | } 12 | > 13 | 14 | 15 |
16 |
17 |
18 |
19 | You're logged in! 20 |
21 |
22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/Pages/Profile/Edit.jsx: -------------------------------------------------------------------------------- 1 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; 2 | import { Head } from '@inertiajs/react'; 3 | import DeleteUserForm from './Partials/DeleteUserForm'; 4 | import UpdatePasswordForm from './Partials/UpdatePasswordForm'; 5 | import UpdateProfileInformationForm from './Partials/UpdateProfileInformationForm'; 6 | 7 | export default function Edit({ mustVerifyEmail, status }) { 8 | return ( 9 | 12 | Profile 13 | 14 | } 15 | > 16 | 17 | 18 |
19 |
20 |
21 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/app.jsx: -------------------------------------------------------------------------------- 1 | import '../css/app.css'; 2 | import './bootstrap'; 3 | 4 | import { createInertiaApp } from '@inertiajs/react'; 5 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 6 | import { createRoot } from 'react-dom/client'; 7 | 8 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 9 | 10 | createInertiaApp({ 11 | title: (title) => `${title} - ${appName}`, 12 | resolve: (name) => 13 | resolvePageComponent( 14 | `./Pages/${name}.jsx`, 15 | import.meta.glob('./Pages/**/*.jsx'), 16 | ), 17 | setup({ el, App, props }) { 18 | const root = createRoot(el); 19 | 20 | root.render(); 21 | }, 22 | progress: { 23 | color: '#4B5563', 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/js/ssr.jsx: -------------------------------------------------------------------------------- 1 | import { createInertiaApp } from '@inertiajs/react'; 2 | import createServer from '@inertiajs/react/server'; 3 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 4 | import ReactDOMServer from 'react-dom/server'; 5 | import { route } from '../../vendor/tightenco/ziggy'; 6 | 7 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 8 | 9 | createServer((page) => 10 | createInertiaApp({ 11 | page, 12 | render: ReactDOMServer.renderToString, 13 | title: (title) => `${title} - ${appName}`, 14 | resolve: (name) => 15 | resolvePageComponent( 16 | `./Pages/${name}.jsx`, 17 | import.meta.glob('./Pages/**/*.jsx'), 18 | ), 19 | setup: ({ App, props }) => { 20 | global.route = (name, params, absolute) => 21 | route(name, params, absolute, { 22 | ...page.props.ziggy, 23 | location: new URL(page.props.ziggy.location), 24 | }); 25 | 26 | return ; 27 | }, 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /stubs/inertia-react/resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ config('app.name', 'Laravel') }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | @routes 15 | @viteReactRefresh 16 | @vite(['resources/js/app.jsx', "resources/js/Pages/{$page['component']}.jsx"]) 17 | @inertiaHead 18 | 19 | 20 | @inertia 21 | 22 | 23 | -------------------------------------------------------------------------------- /stubs/inertia-react/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | import react from '@vitejs/plugin-react'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | laravel({ 8 | input: 'resources/js/app.jsx', 9 | refresh: true, 10 | }), 11 | react(), 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require("@rushstack/eslint-patch/modern-module-resolution") 3 | 4 | module.exports = { 5 | root: true, 6 | extends: [ 7 | "plugin:vue/vue3-essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript", 10 | "@vue/eslint-config-prettier" 11 | ], 12 | parserOptions: { 13 | ecmaVersion: "latest" 14 | }, 15 | rules: { 16 | "vue/multi-word-component-names": "off", 17 | "no-undef": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/Checkbox.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 30 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/DangerButton.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/DropdownLink.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/InputError.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/InputLabel.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/NavLink.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/PrimaryButton.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/ResponsiveNavLink.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/SecondaryButton.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Components/TextInput.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Layouts/GuestLayout.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 23 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Pages/Auth/ConfirmPassword.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 58 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Pages/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 31 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/Pages/Profile/Edit.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 53 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/app.ts: -------------------------------------------------------------------------------- 1 | import '../css/app.css'; 2 | import './bootstrap'; 3 | 4 | import { createInertiaApp } from '@inertiajs/vue3'; 5 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 6 | import { createApp, DefineComponent, h } from 'vue'; 7 | import { ZiggyVue } from '../../vendor/tightenco/ziggy'; 8 | 9 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 10 | 11 | createInertiaApp({ 12 | title: (title) => `${title} - ${appName}`, 13 | resolve: (name) => 14 | resolvePageComponent( 15 | `./Pages/${name}.vue`, 16 | import.meta.glob('./Pages/**/*.vue'), 17 | ), 18 | setup({ el, App, props, plugin }) { 19 | createApp({ render: () => h(App, props) }) 20 | .use(plugin) 21 | .use(ZiggyVue) 22 | .mount(el); 23 | }, 24 | progress: { 25 | color: '#4B5563', 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/ssr.ts: -------------------------------------------------------------------------------- 1 | import { createInertiaApp } from '@inertiajs/vue3'; 2 | import createServer from '@inertiajs/vue3/server'; 3 | import { renderToString } from '@vue/server-renderer'; 4 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 5 | import { createSSRApp, DefineComponent, h } from 'vue'; 6 | import { ZiggyVue } from '../../vendor/tightenco/ziggy'; 7 | 8 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 9 | 10 | createServer((page) => 11 | createInertiaApp({ 12 | page, 13 | render: renderToString, 14 | title: (title) => `${title} - ${appName}`, 15 | resolve: (name) => 16 | resolvePageComponent( 17 | `./Pages/${name}.vue`, 18 | import.meta.glob('./Pages/**/*.vue'), 19 | ), 20 | setup({ App, props, plugin }) { 21 | return createSSRApp({ render: () => h(App, props) }) 22 | .use(plugin) 23 | .use(ZiggyVue, { 24 | ...page.props.ziggy, 25 | location: new URL(page.props.ziggy.location), 26 | }); 27 | }, 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import { PageProps as InertiaPageProps } from '@inertiajs/core'; 2 | import { AxiosInstance } from 'axios'; 3 | import { route as ziggyRoute } from 'ziggy-js'; 4 | import { PageProps as AppPageProps } from './'; 5 | 6 | declare global { 7 | interface Window { 8 | axios: AxiosInstance; 9 | } 10 | 11 | /* eslint-disable no-var */ 12 | var route: typeof ziggyRoute; 13 | } 14 | 15 | declare module 'vue' { 16 | interface ComponentCustomProperties { 17 | route: typeof ziggyRoute; 18 | } 19 | } 20 | 21 | declare module '@inertiajs/core' { 22 | interface PageProps extends InertiaPageProps, AppPageProps {} 23 | } 24 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | name: string; 4 | email: string; 5 | email_verified_at?: string; 6 | } 7 | 8 | export type PageProps< 9 | T extends Record = Record, 10 | > = T & { 11 | auth: { 12 | user: User; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/resources/js/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /stubs/inertia-vue-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "jsx": "preserve", 7 | "strict": true, 8 | "isolatedModules": true, 9 | "target": "ESNext", 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "paths": { 15 | "@/*": ["./resources/js/*"], 16 | "ziggy-js": ["./vendor/tightenco/ziggy"] 17 | } 18 | }, 19 | "include": ["resources/js/**/*.ts", "resources/js/**/*.vue"] 20 | } 21 | -------------------------------------------------------------------------------- /stubs/inertia-vue/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require("@rushstack/eslint-patch/modern-module-resolution") 3 | 4 | module.exports = { 5 | root: true, 6 | extends: [ 7 | "plugin:vue/vue3-essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-prettier" 10 | ], 11 | parserOptions: { 12 | ecmaVersion: "latest" 13 | }, 14 | rules: { 15 | "vue/multi-word-component-names": "off", 16 | "no-undef": "off" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/Checkbox.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/DangerButton.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/DropdownLink.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/InputError.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/InputLabel.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/NavLink.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/PrimaryButton.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/ResponsiveNavLink.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/SecondaryButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Components/TextInput.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Layouts/GuestLayout.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 23 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Pages/Auth/ConfirmPassword.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 56 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Pages/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 31 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/Pages/Profile/Edit.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 57 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import '../css/app.css'; 2 | import './bootstrap'; 3 | 4 | import { createInertiaApp } from '@inertiajs/vue3'; 5 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 6 | import { createApp, h } from 'vue'; 7 | import { ZiggyVue } from '../../vendor/tightenco/ziggy'; 8 | 9 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 10 | 11 | createInertiaApp({ 12 | title: (title) => `${title} - ${appName}`, 13 | resolve: (name) => 14 | resolvePageComponent( 15 | `./Pages/${name}.vue`, 16 | import.meta.glob('./Pages/**/*.vue'), 17 | ), 18 | setup({ el, App, props, plugin }) { 19 | return createApp({ render: () => h(App, props) }) 20 | .use(plugin) 21 | .use(ZiggyVue) 22 | .mount(el); 23 | }, 24 | progress: { 25 | color: '#4B5563', 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/js/ssr.js: -------------------------------------------------------------------------------- 1 | import { createInertiaApp } from '@inertiajs/vue3'; 2 | import createServer from '@inertiajs/vue3/server'; 3 | import { renderToString } from '@vue/server-renderer'; 4 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 5 | import { createSSRApp, h } from 'vue'; 6 | import { ZiggyVue } from '../../vendor/tightenco/ziggy'; 7 | 8 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 9 | 10 | createServer((page) => 11 | createInertiaApp({ 12 | page, 13 | render: renderToString, 14 | title: (title) => `${title} - ${appName}`, 15 | resolve: (name) => 16 | resolvePageComponent( 17 | `./Pages/${name}.vue`, 18 | import.meta.glob('./Pages/**/*.vue'), 19 | ), 20 | setup({ App, props, plugin }) { 21 | return createSSRApp({ render: () => h(App, props) }) 22 | .use(plugin) 23 | .use(ZiggyVue, { 24 | ...page.props.ziggy, 25 | location: new URL(page.props.ziggy.location), 26 | }); 27 | }, 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /stubs/inertia-vue/resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ config('app.name', 'Laravel') }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | @routes 15 | @vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"]) 16 | @inertiaHead 17 | 18 | 19 | @inertia 20 | 21 | 22 | -------------------------------------------------------------------------------- /stubs/inertia-vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | import vue from '@vitejs/plugin-vue'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | laravel({ 8 | input: 'resources/js/app.js', 9 | refresh: true, 10 | }), 11 | vue({ 12 | template: { 13 | transformAssetUrls: { 14 | base: null, 15 | includeAbsolute: false, 16 | }, 17 | }, 18 | }), 19 | ], 20 | }); 21 | -------------------------------------------------------------------------------- /stubs/livewire-common/app/Livewire/Actions/Logout.php: -------------------------------------------------------------------------------- 1 | logout(); 16 | 17 | Session::invalidate(); 18 | Session::regenerateToken(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stubs/livewire-common/app/Livewire/Forms/LoginForm.php: -------------------------------------------------------------------------------- 1 | ensureIsNotRateLimited(); 32 | 33 | if (! Auth::attempt($this->only(['email', 'password']), $this->remember)) { 34 | RateLimiter::hit($this->throttleKey()); 35 | 36 | throw ValidationException::withMessages([ 37 | 'form.email' => trans('auth.failed'), 38 | ]); 39 | } 40 | 41 | RateLimiter::clear($this->throttleKey()); 42 | } 43 | 44 | /** 45 | * Ensure the authentication request is not rate limited. 46 | */ 47 | protected function ensureIsNotRateLimited(): void 48 | { 49 | if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { 50 | return; 51 | } 52 | 53 | event(new Lockout(request())); 54 | 55 | $seconds = RateLimiter::availableIn($this->throttleKey()); 56 | 57 | throw ValidationException::withMessages([ 58 | 'form.email' => trans('auth.throttle', [ 59 | 'seconds' => $seconds, 60 | 'minutes' => ceil($seconds / 60), 61 | ]), 62 | ]); 63 | } 64 | 65 | /** 66 | * Get the authentication rate limiting throttle key. 67 | */ 68 | protected function throttleKey(): string 69 | { 70 | return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 8 | 9 | $response 10 | ->assertOk() 11 | ->assertSeeVolt('pages.auth.login'); 12 | }); 13 | 14 | test('users can authenticate using the login screen', function () { 15 | $user = User::factory()->create(); 16 | 17 | $component = Volt::test('pages.auth.login') 18 | ->set('form.email', $user->email) 19 | ->set('form.password', 'password'); 20 | 21 | $component->call('login'); 22 | 23 | $component 24 | ->assertHasNoErrors() 25 | ->assertRedirect(route('dashboard', absolute: false)); 26 | 27 | $this->assertAuthenticated(); 28 | }); 29 | 30 | test('users can not authenticate with invalid password', function () { 31 | $user = User::factory()->create(); 32 | 33 | $component = Volt::test('pages.auth.login') 34 | ->set('form.email', $user->email) 35 | ->set('form.password', 'wrong-password'); 36 | 37 | $component->call('login'); 38 | 39 | $component 40 | ->assertHasErrors() 41 | ->assertNoRedirect(); 42 | 43 | $this->assertGuest(); 44 | }); 45 | 46 | test('navigation menu can be rendered', function () { 47 | $user = User::factory()->create(); 48 | 49 | $this->actingAs($user); 50 | 51 | $response = $this->get('/dashboard'); 52 | 53 | $response 54 | ->assertOk() 55 | ->assertSeeVolt('layout.navigation'); 56 | }); 57 | 58 | test('users can logout', function () { 59 | $user = User::factory()->create(); 60 | 61 | $this->actingAs($user); 62 | 63 | $component = Volt::test('layout.navigation'); 64 | 65 | $component->call('logout'); 66 | 67 | $component 68 | ->assertHasNoErrors() 69 | ->assertRedirect('/'); 70 | 71 | $this->assertGuest(); 72 | }); 73 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 10 | 11 | $response = $this->actingAs($user)->get('/verify-email'); 12 | 13 | $response->assertStatus(200); 14 | }); 15 | 16 | test('email can be verified', function () { 17 | $user = User::factory()->unverified()->create(); 18 | 19 | Event::fake(); 20 | 21 | $verificationUrl = URL::temporarySignedRoute( 22 | 'verification.verify', 23 | now()->addMinutes(60), 24 | ['id' => $user->id, 'hash' => sha1($user->email)] 25 | ); 26 | 27 | $response = $this->actingAs($user)->get($verificationUrl); 28 | 29 | Event::assertDispatched(Verified::class); 30 | expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); 31 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 32 | }); 33 | 34 | test('email is not verified with invalid hash', function () { 35 | $user = User::factory()->unverified()->create(); 36 | 37 | $verificationUrl = URL::temporarySignedRoute( 38 | 'verification.verify', 39 | now()->addMinutes(60), 40 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 41 | ); 42 | 43 | $this->actingAs($user)->get($verificationUrl); 44 | 45 | expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); 46 | }); 47 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 10 | 11 | $response = $this->actingAs($user)->get('/confirm-password'); 12 | 13 | $response 14 | ->assertSeeVolt('pages.auth.confirm-password') 15 | ->assertStatus(200); 16 | }); 17 | 18 | test('password can be confirmed', function () { 19 | $user = User::factory()->create(); 20 | 21 | $this->actingAs($user); 22 | 23 | $component = Volt::test('pages.auth.confirm-password') 24 | ->set('password', 'password'); 25 | 26 | $component->call('confirmPassword'); 27 | 28 | $component 29 | ->assertRedirect('/dashboard') 30 | ->assertHasNoErrors(); 31 | }); 32 | 33 | test('password is not confirmed with invalid password', function () { 34 | $user = User::factory()->create(); 35 | 36 | $this->actingAs($user); 37 | 38 | $component = Volt::test('pages.auth.confirm-password') 39 | ->set('password', 'wrong-password'); 40 | 41 | $component->call('confirmPassword'); 42 | 43 | $component 44 | ->assertNoRedirect() 45 | ->assertHasErrors('password'); 46 | }); 47 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 11 | 12 | $this->actingAs($user); 13 | 14 | $component = Volt::test('profile.update-password-form') 15 | ->set('current_password', 'password') 16 | ->set('password', 'new-password') 17 | ->set('password_confirmation', 'new-password') 18 | ->call('updatePassword'); 19 | 20 | $component 21 | ->assertHasNoErrors() 22 | ->assertNoRedirect(); 23 | 24 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 25 | }); 26 | 27 | test('correct password must be provided to update password', function () { 28 | $user = User::factory()->create(); 29 | 30 | $this->actingAs($user); 31 | 32 | $component = Volt::test('profile.update-password-form') 33 | ->set('current_password', 'wrong-password') 34 | ->set('password', 'new-password') 35 | ->set('password_confirmation', 'new-password') 36 | ->call('updatePassword'); 37 | 38 | $component 39 | ->assertHasErrors(['current_password']) 40 | ->assertNoRedirect(); 41 | }); 42 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 9 | 10 | $response 11 | ->assertOk() 12 | ->assertSeeVolt('pages.auth.register'); 13 | }); 14 | 15 | test('new users can register', function () { 16 | $component = Volt::test('pages.auth.register') 17 | ->set('name', 'Test User') 18 | ->set('email', 'test@example.com') 19 | ->set('password', 'password') 20 | ->set('password_confirmation', 'password'); 21 | 22 | $component->call('register'); 23 | 24 | $component->assertRedirect(route('dashboard', absolute: false)); 25 | 26 | $this->assertAuthenticated(); 27 | }); 28 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Pest.php: -------------------------------------------------------------------------------- 1 | extend(Tests\TestCase::class) 15 | ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) 16 | ->in('Feature'); 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Expectations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | When you're writing tests, you often need to check that values meet certain conditions. The 24 | | "expect()" function gives you access to a set of "expectations" methods that you can use 25 | | to assert different things. Of course, you may extend the Expectation API at any time. 26 | | 27 | */ 28 | 29 | expect()->extend('toBeOne', function () { 30 | return $this->toBe(1); 31 | }); 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Functions 36 | |-------------------------------------------------------------------------- 37 | | 38 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 39 | | project that you don't want to repeat in every file. Here you can also expose helpers as 40 | | global functions to help you to reduce the number of lines of code in your test files. 41 | | 42 | */ 43 | 44 | function something() 45 | { 46 | // .. 47 | } 48 | -------------------------------------------------------------------------------- /stubs/livewire-common/pest-tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /stubs/livewire-common/resources/views/components/action-message.blade.php: -------------------------------------------------------------------------------- 1 | @props(['on']) 2 | 3 |
merge(['class' => 'text-sm text-gray-600 dark:text-gray-400']) }}> 9 | {{ $slot->isEmpty() ? __('Saved.') : $slot }} 10 |
11 | -------------------------------------------------------------------------------- /stubs/livewire-common/resources/views/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Dashboard') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | {{ __("You're logged in!") }} 13 |
14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /stubs/livewire-common/resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 |
19 | 20 | 21 | 22 | @if (isset($header)) 23 |
24 |
25 | {{ $header }} 26 |
27 |
28 | @endif 29 | 30 | 31 |
32 | {{ $slot }} 33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /stubs/livewire-common/resources/views/layouts/guest.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 | 25 |
26 | {{ $slot }} 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /stubs/livewire-common/resources/views/profile.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Profile') }} 5 |

6 |
7 | 8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /stubs/livewire-common/routes/auth.php: -------------------------------------------------------------------------------- 1 | group(function () { 8 | Volt::route('register', 'pages.auth.register') 9 | ->name('register'); 10 | 11 | Volt::route('login', 'pages.auth.login') 12 | ->name('login'); 13 | 14 | Volt::route('forgot-password', 'pages.auth.forgot-password') 15 | ->name('password.request'); 16 | 17 | Volt::route('reset-password/{token}', 'pages.auth.reset-password') 18 | ->name('password.reset'); 19 | }); 20 | 21 | Route::middleware('auth')->group(function () { 22 | Volt::route('verify-email', 'pages.auth.verify-email') 23 | ->name('verification.notice'); 24 | 25 | Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) 26 | ->middleware(['signed', 'throttle:6,1']) 27 | ->name('verification.verify'); 28 | 29 | Volt::route('confirm-password', 'pages.auth.confirm-password') 30 | ->name('password.confirm'); 31 | }); 32 | -------------------------------------------------------------------------------- /stubs/livewire-common/routes/web.php: -------------------------------------------------------------------------------- 1 | middleware(['auth', 'verified']) 9 | ->name('dashboard'); 10 | 11 | Route::view('profile', 'profile') 12 | ->middleware(['auth']) 13 | ->name('profile'); 14 | 15 | require __DIR__.'/auth.php'; 16 | -------------------------------------------------------------------------------- /stubs/livewire-common/tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 19 | 20 | $response = $this->actingAs($user)->get('/verify-email'); 21 | 22 | $response 23 | ->assertSeeVolt('pages.auth.verify-email') 24 | ->assertStatus(200); 25 | } 26 | 27 | public function test_email_can_be_verified(): void 28 | { 29 | $user = User::factory()->unverified()->create(); 30 | 31 | Event::fake(); 32 | 33 | $verificationUrl = URL::temporarySignedRoute( 34 | 'verification.verify', 35 | now()->addMinutes(60), 36 | ['id' => $user->id, 'hash' => sha1($user->email)] 37 | ); 38 | 39 | $response = $this->actingAs($user)->get($verificationUrl); 40 | 41 | Event::assertDispatched(Verified::class); 42 | $this->assertTrue($user->fresh()->hasVerifiedEmail()); 43 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 44 | } 45 | 46 | public function test_email_is_not_verified_with_invalid_hash(): void 47 | { 48 | $user = User::factory()->unverified()->create(); 49 | 50 | $verificationUrl = URL::temporarySignedRoute( 51 | 'verification.verify', 52 | now()->addMinutes(60), 53 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 54 | ); 55 | 56 | $this->actingAs($user)->get($verificationUrl); 57 | 58 | $this->assertFalse($user->fresh()->hasVerifiedEmail()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /stubs/livewire-common/tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | $response = $this->actingAs($user)->get('/confirm-password'); 19 | 20 | $response 21 | ->assertSeeVolt('pages.auth.confirm-password') 22 | ->assertStatus(200); 23 | } 24 | 25 | public function test_password_can_be_confirmed(): void 26 | { 27 | $user = User::factory()->create(); 28 | 29 | $this->actingAs($user); 30 | 31 | $component = Volt::test('pages.auth.confirm-password') 32 | ->set('password', 'password'); 33 | 34 | $component->call('confirmPassword'); 35 | 36 | $component 37 | ->assertRedirect('/dashboard') 38 | ->assertHasNoErrors(); 39 | } 40 | 41 | public function test_password_is_not_confirmed_with_invalid_password(): void 42 | { 43 | $user = User::factory()->create(); 44 | 45 | $this->actingAs($user); 46 | 47 | $component = Volt::test('pages.auth.confirm-password') 48 | ->set('password', 'wrong-password'); 49 | 50 | $component->call('confirmPassword'); 51 | 52 | $component 53 | ->assertNoRedirect() 54 | ->assertHasErrors('password'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /stubs/livewire-common/tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 18 | 19 | $this->actingAs($user); 20 | 21 | $component = Volt::test('profile.update-password-form') 22 | ->set('current_password', 'password') 23 | ->set('password', 'new-password') 24 | ->set('password_confirmation', 'new-password') 25 | ->call('updatePassword'); 26 | 27 | $component 28 | ->assertHasNoErrors() 29 | ->assertNoRedirect(); 30 | 31 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 32 | } 33 | 34 | public function test_correct_password_must_be_provided_to_update_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $this->actingAs($user); 39 | 40 | $component = Volt::test('profile.update-password-form') 41 | ->set('current_password', 'wrong-password') 42 | ->set('password', 'new-password') 43 | ->set('password_confirmation', 'new-password') 44 | ->call('updatePassword'); 45 | 46 | $component 47 | ->assertHasErrors(['current_password']) 48 | ->assertNoRedirect(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /stubs/livewire-common/tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 16 | 17 | $response 18 | ->assertOk() 19 | ->assertSeeVolt('pages.auth.register'); 20 | } 21 | 22 | public function test_new_users_can_register(): void 23 | { 24 | $component = Volt::test('pages.auth.register') 25 | ->set('name', 'Test User') 26 | ->set('email', 'test@example.com') 27 | ->set('password', 'password') 28 | ->set('password_confirmation', 'password'); 29 | 30 | $component->call('register'); 31 | 32 | $component->assertRedirect(route('dashboard', absolute: false)); 33 | 34 | $this->assertAuthenticated(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php: -------------------------------------------------------------------------------- 1 | '']); 13 | 14 | rules(['password' => ['required', 'string']]); 15 | 16 | $confirmPassword = function () { 17 | $this->validate(); 18 | 19 | if (! Auth::guard('web')->validate([ 20 | 'email' => Auth::user()->email, 21 | 'password' => $this->password, 22 | ])) { 23 | throw ValidationException::withMessages([ 24 | 'password' => __('auth.password'), 25 | ]); 26 | } 27 | 28 | session(['auth.password_confirmed_at' => time()]); 29 | 30 | $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); 31 | }; 32 | 33 | ?> 34 | 35 |
36 |
37 | {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} 38 |
39 | 40 |
41 | 42 |
43 | 44 | 45 | 51 | 52 | 53 |
54 | 55 |
56 | 57 | {{ __('Confirm') }} 58 | 59 |
60 |
61 |
62 | -------------------------------------------------------------------------------- /stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | '']); 13 | 14 | rules(['email' => ['required', 'string', 'email']]); 15 | 16 | $sendPasswordResetLink = function () { 17 | $this->validate(); 18 | 19 | // We will send the password reset link to this user. Once we have attempted 20 | // to send the link, we will examine the response then see the message we 21 | // need to show to the user. Finally, we'll send out a proper response. 22 | $status = Password::sendResetLink( 23 | $this->only('email') 24 | ); 25 | 26 | if ($status != Password::RESET_LINK_SENT) { 27 | $this->addError('email', __($status)); 28 | 29 | return; 30 | } 31 | 32 | $this->reset('email'); 33 | 34 | Session::flash('status', __($status)); 35 | }; 36 | 37 | ?> 38 | 39 |
40 |
41 | {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} 42 |
43 | 44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 |
56 | 57 | {{ __('Email Password Reset Link') }} 58 | 59 |
60 |
61 |
62 | -------------------------------------------------------------------------------- /stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php: -------------------------------------------------------------------------------- 1 | hasVerifiedEmail()) { 13 | $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); 14 | 15 | return; 16 | } 17 | 18 | Auth::user()->sendEmailVerificationNotification(); 19 | 20 | Session::flash('status', 'verification-link-sent'); 21 | }; 22 | 23 | $logout = function (Logout $logout) { 24 | $logout(); 25 | 26 | $this->redirect('/', navigate: true); 27 | }; 28 | 29 | ?> 30 | 31 |
32 |
33 | {{ __('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.') }} 34 |
35 | 36 | @if (session('status') == 'verification-link-sent') 37 |
38 | {{ __('A new verification link has been sent to the email address you provided during registration.') }} 39 |
40 | @endif 41 | 42 |
43 | 44 | {{ __('Resend Verification Email') }} 45 | 46 | 47 | 50 |
51 |
52 | -------------------------------------------------------------------------------- /stubs/livewire-functional/resources/views/livewire/welcome/navigation.blade.php: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php: -------------------------------------------------------------------------------- 1 | validate([ 18 | 'password' => ['required', 'string'], 19 | ]); 20 | 21 | if (! Auth::guard('web')->validate([ 22 | 'email' => Auth::user()->email, 23 | 'password' => $this->password, 24 | ])) { 25 | throw ValidationException::withMessages([ 26 | 'password' => __('auth.password'), 27 | ]); 28 | } 29 | 30 | session(['auth.password_confirmed_at' => time()]); 31 | 32 | $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); 33 | } 34 | }; ?> 35 | 36 |
37 |
38 | {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} 39 |
40 | 41 |
42 | 43 |
44 | 45 | 46 | 52 | 53 | 54 |
55 | 56 |
57 | 58 | {{ __('Confirm') }} 59 | 60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /stubs/livewire/resources/views/livewire/welcome/navigation.blade.php: -------------------------------------------------------------------------------- 1 | 27 | --------------------------------------------------------------------------------