├── 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 |
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 |
25 |
26 |
--------------------------------------------------------------------------------
/stubs/default/resources/views/auth/reset-password.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
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 |
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 | merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
2 | {{ $slot }}
3 |
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 |
31 |
32 | {{ $content }}
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/stubs/default/resources/views/components/input-error.blade.php:
--------------------------------------------------------------------------------
1 | @props(['messages'])
2 |
3 | @if ($messages)
4 | merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}>
5 | @foreach ((array) $messages as $message)
6 | {{ $message }}
7 | @endforeach
8 |
9 | @endif
10 |
--------------------------------------------------------------------------------
/stubs/default/resources/views/components/input-label.blade.php:
--------------------------------------------------------------------------------
1 | @props(['value'])
2 |
3 | merge(['class' => 'block font-medium text-sm text-gray-700 dark:text-gray-300']) }}>
4 | {{ $value ?? $slot }}
5 |
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 | merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
2 | {{ $slot }}
3 |
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 | merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150']) }}>
2 | {{ $slot }}
3 |
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 |
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 |
19 | {children}
20 |
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 |
17 | {value ? value : children}
18 |
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 |
19 | {children}
20 |
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 |
21 | {children}
22 |
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 |
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 |
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 |
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 |
17 | {children}
18 |
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 |
15 | {value ? value : children}
16 |
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 |
17 | {children}
18 |
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 |
19 | {children}
20 |
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 |
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 |
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 |
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 |
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 |
23 |
29 |
30 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/DangerButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/DropdownLink.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/InputError.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 | {{ message }}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/InputLabel.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 | {{ value }}
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/NavLink.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/PrimaryButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/ResponsiveNavLink.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/SecondaryButton.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Components/TextInput.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
23 |
24 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Layouts/GuestLayout.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
10 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Pages/Auth/ConfirmPassword.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | This is a secure area of the application. Please confirm your
28 | password before continuing.
29 |
30 |
31 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Pages/Dashboard.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 | Dashboard
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 | You're logged in!
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/stubs/inertia-vue-ts/resources/js/Pages/Profile/Edit.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 | Profile
23 |
24 |
25 |
26 |
27 |
28 |
31 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
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 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/DangerButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/DropdownLink.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/InputError.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 | {{ message }}
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/InputLabel.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {{ value }}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/NavLink.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/PrimaryButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/ResponsiveNavLink.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/SecondaryButton.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Components/TextInput.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Layouts/GuestLayout.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
10 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Pages/Auth/ConfirmPassword.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | This is a secure area of the application. Please confirm your
26 | password before continuing.
27 |
28 |
29 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Pages/Dashboard.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 | Dashboard
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 | You're logged in!
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/stubs/inertia-vue/resources/js/Pages/Profile/Edit.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 | Profile
27 |
28 |
29 |
30 |
31 |
32 |
35 |
40 |
41 |
42 |
45 |
46 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
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 |
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 |
15 |
16 |
21 |
22 |
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 |
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 |
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 |
48 | {{ __('Log Out') }}
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/stubs/livewire-functional/resources/views/livewire/welcome/navigation.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @auth
3 |
7 | Dashboard
8 |
9 | @else
10 |
14 | Log in
15 |
16 |
17 | @if (Route::has('register'))
18 |
22 | Register
23 |
24 | @endif
25 | @endauth
26 |
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 |
62 |
63 |
--------------------------------------------------------------------------------
/stubs/livewire/resources/views/livewire/welcome/navigation.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @auth
3 |
7 | Dashboard
8 |
9 | @else
10 |
14 | Log in
15 |
16 |
17 | @if (Route::has('register'))
18 |
22 | Register
23 |
24 | @endif
25 | @endauth
26 |
27 |
--------------------------------------------------------------------------------