From 63291aba76e1d99cf9f30b0e9ec0913a24299cd6 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sun, 2 Feb 2025 23:36:34 +0100 Subject: [PATCH 01/16] feat/livewire init --- src/Console/InstallCommand.php | 20 +- src/Console/InstallModuleLivewire.php | 173 ++++++++++++++++++ .../app/Livewire/Actions/Logout.php | 20 ++ .../app/Livewire/Forms/LoginForm.php | 72 ++++++++ .../Feature/Auth/AuthenticationTest.php | 72 ++++++++ .../Feature/Auth/EmailVerificationTest.php | 46 +++++ .../Feature/Auth/PasswordConfirmationTest.php | 46 +++++ .../Feature/Auth/PasswordResetTest.php | 73 ++++++++ .../Feature/Auth/PasswordUpdateTest.php | 41 +++++ .../Feature/Auth/RegistrationTest.php | 27 +++ .../pest-tests/Feature/ExampleTest.php | 7 + .../pest-tests/Feature/ProfileTest.php | 89 +++++++++ stubs/livewire-common/pest-tests/Pest.php | 47 +++++ .../pest-tests/Unit/ExampleTest.php | 5 + .../views/components/action-message.blade.php | 10 + .../resources/views/dashboard.blade.php | 17 ++ .../resources/views/layouts/app.blade.php | 36 ++++ .../resources/views/layouts/guest.blade.php | 30 +++ .../resources/views/profile.blade.php | 29 +++ .../resources/views/welcome.blade.php | 145 +++++++++++++++ stubs/livewire-common/routes/auth.php | 31 ++++ stubs/livewire-common/routes/web.php | 15 ++ .../tests/Feature/Auth/AuthenticationTest.php | 86 +++++++++ .../Feature/Auth/EmailVerificationTest.php | 60 ++++++ .../Feature/Auth/PasswordConfirmationTest.php | 56 ++++++ .../tests/Feature/Auth/PasswordResetTest.php | 84 +++++++++ .../tests/Feature/Auth/PasswordUpdateTest.php | 50 +++++ .../tests/Feature/Auth/RegistrationTest.php | 36 ++++ .../tests/Feature/ProfileTest.php | 101 ++++++++++ .../livewire/layout/navigation.blade.php | 104 +++++++++++ .../pages/auth/confirm-password.blade.php | 61 ++++++ .../pages/auth/forgot-password.blade.php | 61 ++++++ .../views/livewire/pages/auth/login.blade.php | 69 +++++++ .../livewire/pages/auth/register.blade.php | 91 +++++++++ .../pages/auth/reset-password.blade.php | 97 ++++++++++ .../pages/auth/verify-email.blade.php | 51 ++++++ .../profile/delete-user-form.blade.php | 76 ++++++++ .../profile/update-password-form.blade.php | 80 ++++++++ .../update-profile-information-form.blade.php | 101 ++++++++++ .../livewire/welcome/navigation.blade.php | 26 +++ .../livewire/layout/navigation.blade.php | 110 +++++++++++ .../pages/auth/confirm-password.blade.php | 62 +++++++ .../pages/auth/forgot-password.blade.php | 61 ++++++ .../views/livewire/pages/auth/login.blade.php | 71 +++++++ .../livewire/pages/auth/register.blade.php | 88 +++++++++ .../pages/auth/reset-password.blade.php | 105 +++++++++++ .../pages/auth/verify-email.blade.php | 58 ++++++ .../profile/delete-user-form.blade.php | 79 ++++++++ .../profile/update-password-form.blade.php | 79 ++++++++ .../update-profile-information-form.blade.php | 115 ++++++++++++ .../livewire/welcome/navigation.blade.php | 26 +++ 51 files changed, 3190 insertions(+), 5 deletions(-) create mode 100644 src/Console/InstallModuleLivewire.php create mode 100644 stubs/livewire-common/app/Livewire/Actions/Logout.php create mode 100644 stubs/livewire-common/app/Livewire/Forms/LoginForm.php create mode 100644 stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/Auth/EmailVerificationTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/Auth/PasswordConfirmationTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/Auth/PasswordResetTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/Auth/PasswordUpdateTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/Auth/RegistrationTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/ExampleTest.php create mode 100644 stubs/livewire-common/pest-tests/Feature/ProfileTest.php create mode 100644 stubs/livewire-common/pest-tests/Pest.php create mode 100644 stubs/livewire-common/pest-tests/Unit/ExampleTest.php create mode 100644 stubs/livewire-common/resources/views/components/action-message.blade.php create mode 100644 stubs/livewire-common/resources/views/dashboard.blade.php create mode 100644 stubs/livewire-common/resources/views/layouts/app.blade.php create mode 100644 stubs/livewire-common/resources/views/layouts/guest.blade.php create mode 100644 stubs/livewire-common/resources/views/profile.blade.php create mode 100644 stubs/livewire-common/resources/views/welcome.blade.php create mode 100644 stubs/livewire-common/routes/auth.php create mode 100644 stubs/livewire-common/routes/web.php create mode 100644 stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php create mode 100644 stubs/livewire-common/tests/Feature/Auth/EmailVerificationTest.php create mode 100644 stubs/livewire-common/tests/Feature/Auth/PasswordConfirmationTest.php create mode 100644 stubs/livewire-common/tests/Feature/Auth/PasswordResetTest.php create mode 100644 stubs/livewire-common/tests/Feature/Auth/PasswordUpdateTest.php create mode 100644 stubs/livewire-common/tests/Feature/Auth/RegistrationTest.php create mode 100644 stubs/livewire-common/tests/Feature/ProfileTest.php create mode 100644 stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php create mode 100644 stubs/livewire-functional/resources/views/livewire/welcome/navigation.blade.php create mode 100644 stubs/livewire/resources/views/livewire/layout/navigation.blade.php create mode 100644 stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php create mode 100644 stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php create mode 100644 stubs/livewire/resources/views/livewire/pages/auth/login.blade.php create mode 100644 stubs/livewire/resources/views/livewire/pages/auth/register.blade.php create mode 100644 stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php create mode 100644 stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php create mode 100644 stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php create mode 100644 stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php create mode 100644 stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php create mode 100644 stubs/livewire/resources/views/livewire/welcome/navigation.blade.php diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index e645284..7098b7e 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -27,7 +27,7 @@ #[AsCommand(name: 'flextra:install')] final class InstallCommand extends Command implements PromptsForMissingInput { - use InstallModuleBlade, InstallReactWithInertia, InstallSvelteWithInertia, InstallVueWithInertia; + use InstallModuleLivewire, InstallModuleBlade, InstallReactWithInertia, InstallSvelteWithInertia, InstallVueWithInertia; /** * The console command signature and name. @@ -78,7 +78,15 @@ public function handle(): ?int return $this->installBladeModule($this->moduleName); } - $this->components->error('Invalid stack. Supported stacks are [react], [vue], [svelte] and [blade].'); + if ($this->argument('stack') === 'livewire') { + return $this->installLivewireModule($this->moduleName, false); + } + + if ($this->argument('stack') === 'livewire-functional') { + return $this->installLivewireModule($this->moduleName, true); + } + + $this->components->error('Invalid stack. Supported stacks are [react], [vue], [svelte], [livewire], [livewire-functional] and [blade].'); return 1; } @@ -157,7 +165,8 @@ protected function promptForMissingArgumentsUsing(): array 'react' => 'Inertia React with Laravel Modules', 'vue' => 'Inertia Vue with Laravel Modules', 'svelte' => 'Inertia Svelte with Laravel Modules', - 'livewire' => 'Livewire with Laravel Modules', + 'livewire' => 'Livewire (Volt Class API) with Alpine and Laravel Modules', + 'livewire-functional' => 'Livewire (Functional API) with Alpine and Laravel Modules', 'api' => 'API with Laravel Modules', ], scroll: 6, @@ -192,7 +201,7 @@ protected function afterPromptingForMissingArguments(InputInterface $input, Outp ], hint: 'Use the space bar to select options.' ))->each(fn ($option) => $input->setOption($option, true)); - } elseif (in_array($stack, ['blade', 'livewire'])) { + } elseif (in_array($stack, ['blade', 'livewire', 'livewire-functional'])) { $input->setOption('dark', confirm( label: 'Would you like dark mode support?', default: false @@ -215,7 +224,8 @@ protected function installTests(): bool $stubStack = match ($this->argument('stack')) { 'api' => 'api', - 'livewire' => 'livewire', + 'livewire' => 'livewire-common', + 'livewire-functional' => 'livewire-common', 'blade' => 'blade-module', default => 'inertia-php', }; diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php new file mode 100644 index 0000000..a275b00 --- /dev/null +++ b/src/Console/InstallModuleLivewire.php @@ -0,0 +1,173 @@ +updateNodePackages(function ($packages) { + return [ + '@tailwindcss/forms' => '^0.5.2', + 'autoprefixer' => '^10.4.2', + 'postcss' => '^8.4.31', + 'tailwindcss' => '^3.1.0', + ] + $packages; + }); + + // Install Livewire... + if (! $this->requireComposerPackages(['livewire/livewire:^3.4', 'livewire/volt:^1.0'])) { + return 1; + } + + // Install Volt... + (new Process([$this->phpBinary(), 'artisan', 'volt:install'], base_path())) + ->setTimeout(null) + ->run(); + + // Controllers + $this->copyFileWithNamespace( + $moduleName, + __DIR__.'/../../stubs/blade-module/app/Http/Controllers/Auth/VerifyEmailController.php', + base_path('Modules/'.$moduleName.'/app/Http/Controllers/Auth/VerifyEmailController.php') + ); + + // Views... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views', + base_path('Modules/'.$moduleName.'/resources/views') + ); + + // Livewire Components... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/'($functional ? 'livewire-functional' : 'livewire') + .'/resources/views/livewire', + base_path('Modules/'.$moduleName.'/resources/views/livewire') + ); + + // Views Components... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/blade-module/resources/views/components', + base_path('Modules/'.$moduleName.'/resources/views/components') + ); + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views/components', + base_path('Modules/'.$moduleName.'/resources/views/components') + ); + + // Views Layouts... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views/layouts', + base_path('Modules/'.$moduleName.'/resources/views/layouts') + ); + + // Components... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/blade-module/app/View/Components', + base_path('Modules/'.$moduleName.'/app/View/Components') + ); + + // Actions... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/livewire-common/app/Livewire/Actions', + base_path('Modules/'.$moduleName.'/app/Livewire/Actions') + ); + + // Forms... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/livewire-common/app/Livewire/Forms', + base_path('Modules/'.$moduleName.'/app/Livewire/Forms') + ); + + // Dark mode... + if (! $this->option('dark')) { + $this->removeDarkClasses((new Finder) + ->in(base_path('Modules/'.$moduleName.'/resources/views')) + ->name('*.blade.php') + ->notPath('livewire/welcome/navigation.blade.php') + ->notName('welcome.blade.php') + ); + } + + // Tests... + if (! $this->installTests()) { + return 1; + } + + // Routes... + $this->copyModuleFilesWithNamespaceLivewire( + $moduleName, + __DIR__.'/../../stubs/livewire-common/routes', + base_path('Modules/'.$moduleName.'/routes') + ); + // Tailwind / Vite... + copy(__DIR__.'/../../stubs/blade-module/tailwind.config.js', base_path('tailwind.config.js')); + copy(__DIR__.'/../../stubs/blade-module/postcss.config.js', base_path('postcss.config.js')); + copy(__DIR__.'/../../stubs/blade-module/vite.config.js', base_path('vite.config.js')); + copy(__DIR__.'/../../stubs/blade-module/resources/css/app.css', resource_path('css/app.css')); + + $this->components->info('Installing and building Node dependencies.'); + + if (file_exists(base_path('pnpm-lock.yaml'))) { + $this->runCommands(['pnpm install', 'pnpm run build']); + } elseif (file_exists(base_path('yarn.lock'))) { + $this->runCommands(['yarn install', 'yarn run build']); + } elseif (file_exists(base_path('bun.lock')) || file_exists(base_path('bun.lockb'))) { + $this->runCommands(['bun install', 'bun run build']); + } elseif (file_exists(base_path('deno.lock'))) { + $this->runCommands(['deno install', 'deno task build']); + } else { + $this->runCommands(['npm install', 'npm run build']); + } + + $this->components->info('Flextra Modules '.$moduleName.' Livewire scaffolding installed successfully.'); + } + + private function copyModuleFilesWithNamespaceLivewire(string $moduleName, string $stubPath, string $targetPath): void + { + $filesystem = new Filesystem; + $filesystem->ensureDirectoryExists($targetPath); + + // Get all files from the stub path, recursively + $files = (new Filesystem)->allFiles($stubPath); + + foreach ($files as $file) { + // Get the relative path of the file within the stub directory + $relativePath = $file->getRelativePath(); + + // Create the target directory structure if it doesn't exist + $targetDir = $targetPath.($relativePath ? '/'.$relativePath : ''); + $filesystem->ensureDirectoryExists($targetDir); + + // Read and replace placeholders in the file contents + $contents = file_get_contents($file->getPathname()); + $contents = str_replace('{{moduleName}}', $moduleName, $contents); + $contents = str_replace('{{moduleNameLower}}', strtolower($moduleName), $contents); + + // Write the modified contents to the target file, preserving the directory structure + $filesystem->put( + $targetDir.'/'.$file->getFilename(), + $contents + ); + } + } +} diff --git a/stubs/livewire-common/app/Livewire/Actions/Logout.php b/stubs/livewire-common/app/Livewire/Actions/Logout.php new file mode 100644 index 0000000..3ef481d --- /dev/null +++ b/stubs/livewire-common/app/Livewire/Actions/Logout.php @@ -0,0 +1,20 @@ +logout(); + + Session::invalidate(); + Session::regenerateToken(); + } +} diff --git a/stubs/livewire-common/app/Livewire/Forms/LoginForm.php b/stubs/livewire-common/app/Livewire/Forms/LoginForm.php new file mode 100644 index 0000000..3c20c37 --- /dev/null +++ b/stubs/livewire-common/app/Livewire/Forms/LoginForm.php @@ -0,0 +1,72 @@ +ensureIsNotRateLimited(); + + if (! Auth::attempt($this->only(['email', 'password']), $this->remember)) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'form.email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + /** + * Ensure the authentication request is not rate limited. + */ + protected function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout(request())); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'form.email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + /** + * Get the authentication rate limiting throttle key. + */ + protected function throttleKey(): string + { + return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); + } +} diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php new file mode 100644 index 0000000..ec9910f --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/Auth/AuthenticationTest.php @@ -0,0 +1,72 @@ +get('/login'); + + $response + ->assertOk() + ->assertSeeVolt('pages.auth.login'); +}); + +test('users can authenticate using the login screen', function () { + $user = User::factory()->create(); + + $component = Volt::test('pages.auth.login') + ->set('form.email', $user->email) + ->set('form.password', 'password'); + + $component->call('login'); + + $component + ->assertHasNoErrors() + ->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); +}); + +test('users can not authenticate with invalid password', function () { + $user = User::factory()->create(); + + $component = Volt::test('pages.auth.login') + ->set('form.email', $user->email) + ->set('form.password', 'wrong-password'); + + $component->call('login'); + + $component + ->assertHasErrors() + ->assertNoRedirect(); + + $this->assertGuest(); +}); + +test('navigation menu can be rendered', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $response = $this->get('/dashboard'); + + $response + ->assertOk() + ->assertSeeVolt('layout.navigation'); +}); + +test('users can logout', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('layout.navigation'); + + $component->call('logout'); + + $component + ->assertHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/EmailVerificationTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/EmailVerificationTest.php new file mode 100644 index 0000000..f282dff --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/Auth/EmailVerificationTest.php @@ -0,0 +1,46 @@ +unverified()->create(); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response->assertStatus(200); +}); + +test('email can be verified', function () { + $user = User::factory()->unverified()->create(); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); + $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); +}); + +test('email is not verified with invalid hash', function () { + $user = User::factory()->unverified()->create(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/PasswordConfirmationTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/PasswordConfirmationTest.php new file mode 100644 index 0000000..21c28c3 --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/Auth/PasswordConfirmationTest.php @@ -0,0 +1,46 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response + ->assertSeeVolt('pages.auth.confirm-password') + ->assertStatus(200); +}); + +test('password can be confirmed', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('pages.auth.confirm-password') + ->set('password', 'password'); + + $component->call('confirmPassword'); + + $component + ->assertRedirect('/dashboard') + ->assertHasNoErrors(); +}); + +test('password is not confirmed with invalid password', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('pages.auth.confirm-password') + ->set('password', 'wrong-password'); + + $component->call('confirmPassword'); + + $component + ->assertNoRedirect() + ->assertHasErrors('password'); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/PasswordResetTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/PasswordResetTest.php new file mode 100644 index 0000000..c478bce --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/Auth/PasswordResetTest.php @@ -0,0 +1,73 @@ +get('/forgot-password'); + + $response + ->assertSeeVolt('pages.auth.forgot-password') + ->assertStatus(200); +}); + +test('reset password link can be requested', function () { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class); +}); + +test('reset password screen can be rendered', function () { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response + ->assertSeeVolt('pages.auth.reset-password') + ->assertStatus(200); + + return true; + }); +}); + +test('password can be reset with valid token', function () { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $component = Volt::test('pages.auth.reset-password', ['token' => $notification->token]) + ->set('email', $user->email) + ->set('password', 'password') + ->set('password_confirmation', 'password'); + + $component->call('resetPassword'); + + $component + ->assertRedirect('/login') + ->assertHasNoErrors(); + + return true; + }); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/PasswordUpdateTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/PasswordUpdateTest.php new file mode 100644 index 0000000..33b1d4b --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/Auth/PasswordUpdateTest.php @@ -0,0 +1,41 @@ +create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-password-form') + ->set('current_password', 'password') + ->set('password', 'new-password') + ->set('password_confirmation', 'new-password') + ->call('updatePassword'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); +}); + +test('correct password must be provided to update password', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-password-form') + ->set('current_password', 'wrong-password') + ->set('password', 'new-password') + ->set('password_confirmation', 'new-password') + ->call('updatePassword'); + + $component + ->assertHasErrors(['current_password']) + ->assertNoRedirect(); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/Auth/RegistrationTest.php b/stubs/livewire-common/pest-tests/Feature/Auth/RegistrationTest.php new file mode 100644 index 0000000..5d50039 --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/Auth/RegistrationTest.php @@ -0,0 +1,27 @@ +get('/register'); + + $response + ->assertOk() + ->assertSeeVolt('pages.auth.register'); +}); + +test('new users can register', function () { + $component = Volt::test('pages.auth.register') + ->set('name', 'Test User') + ->set('email', 'test@example.com') + ->set('password', 'password') + ->set('password_confirmation', 'password'); + + $component->call('register'); + + $component->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/ExampleTest.php b/stubs/livewire-common/pest-tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8b5843f --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/ExampleTest.php @@ -0,0 +1,7 @@ +get('/'); + + $response->assertStatus(200); +}); diff --git a/stubs/livewire-common/pest-tests/Feature/ProfileTest.php b/stubs/livewire-common/pest-tests/Feature/ProfileTest.php new file mode 100644 index 0000000..984f050 --- /dev/null +++ b/stubs/livewire-common/pest-tests/Feature/ProfileTest.php @@ -0,0 +1,89 @@ +create(); + + $this->actingAs($user); + + $response = $this->get('/profile'); + + $response + ->assertOk() + ->assertSeeVolt('profile.update-profile-information-form') + ->assertSeeVolt('profile.update-password-form') + ->assertSeeVolt('profile.delete-user-form'); +}); + +test('profile information can be updated', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-profile-information-form') + ->set('name', 'Test User') + ->set('email', 'test@example.com') + ->call('updateProfileInformation'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $user->refresh(); + + $this->assertSame('Test User', $user->name); + $this->assertSame('test@example.com', $user->email); + $this->assertNull($user->email_verified_at); +}); + +test('email verification status is unchanged when the email address is unchanged', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-profile-information-form') + ->set('name', 'Test User') + ->set('email', $user->email) + ->call('updateProfileInformation'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $this->assertNotNull($user->refresh()->email_verified_at); +}); + +test('user can delete their account', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.delete-user-form') + ->set('password', 'password') + ->call('deleteUser'); + + $component + ->assertHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); + $this->assertNull($user->fresh()); +}); + +test('correct password must be provided to delete account', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.delete-user-form') + ->set('password', 'wrong-password') + ->call('deleteUser'); + + $component + ->assertHasErrors('password') + ->assertNoRedirect(); + + $this->assertNotNull($user->fresh()); +}); diff --git a/stubs/livewire-common/pest-tests/Pest.php b/stubs/livewire-common/pest-tests/Pest.php new file mode 100644 index 0000000..40d096b --- /dev/null +++ b/stubs/livewire-common/pest-tests/Pest.php @@ -0,0 +1,47 @@ +extend(Tests\TestCase::class) + ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) + ->in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function something() +{ + // .. +} diff --git a/stubs/livewire-common/pest-tests/Unit/ExampleTest.php b/stubs/livewire-common/pest-tests/Unit/ExampleTest.php new file mode 100644 index 0000000..44a4f33 --- /dev/null +++ b/stubs/livewire-common/pest-tests/Unit/ExampleTest.php @@ -0,0 +1,5 @@ +toBeTrue(); +}); diff --git a/stubs/livewire-common/resources/views/components/action-message.blade.php b/stubs/livewire-common/resources/views/components/action-message.blade.php new file mode 100644 index 0000000..fe21e63 --- /dev/null +++ b/stubs/livewire-common/resources/views/components/action-message.blade.php @@ -0,0 +1,10 @@ +@props(['on']) + +
merge(['class' => 'text-sm text-gray-600 dark:text-gray-400']) }}> + {{ $slot->isEmpty() ? __('Saved.') : $slot }} +
diff --git a/stubs/livewire-common/resources/views/dashboard.blade.php b/stubs/livewire-common/resources/views/dashboard.blade.php new file mode 100644 index 0000000..4024c64 --- /dev/null +++ b/stubs/livewire-common/resources/views/dashboard.blade.php @@ -0,0 +1,17 @@ + + +

+ {{ __('Dashboard') }} +

+
+ +
+
+
+
+ {{ __("You're logged in!") }} +
+
+
+
+
diff --git a/stubs/livewire-common/resources/views/layouts/app.blade.php b/stubs/livewire-common/resources/views/layouts/app.blade.php new file mode 100644 index 0000000..89c7cd5 --- /dev/null +++ b/stubs/livewire-common/resources/views/layouts/app.blade.php @@ -0,0 +1,36 @@ + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + +
+ + + + @if (isset($header)) +
+
+ {{ $header }} +
+
+ @endif + + +
+ {{ $slot }} +
+
+ + diff --git a/stubs/livewire-common/resources/views/layouts/guest.blade.php b/stubs/livewire-common/resources/views/layouts/guest.blade.php new file mode 100644 index 0000000..eaa6065 --- /dev/null +++ b/stubs/livewire-common/resources/views/layouts/guest.blade.php @@ -0,0 +1,30 @@ + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + +
+
+ + + +
+ +
+ {{ $slot }} +
+
+ + diff --git a/stubs/livewire-common/resources/views/profile.blade.php b/stubs/livewire-common/resources/views/profile.blade.php new file mode 100644 index 0000000..b14bcc1 --- /dev/null +++ b/stubs/livewire-common/resources/views/profile.blade.php @@ -0,0 +1,29 @@ + + +

+ {{ __('Profile') }} +

+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
diff --git a/stubs/livewire-common/resources/views/welcome.blade.php b/stubs/livewire-common/resources/views/welcome.blade.php new file mode 100644 index 0000000..0464add --- /dev/null +++ b/stubs/livewire-common/resources/views/welcome.blade.php @@ -0,0 +1,145 @@ + + + + + + + Laravel + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + +
+ + +
+ + diff --git a/stubs/livewire-common/routes/auth.php b/stubs/livewire-common/routes/auth.php new file mode 100644 index 0000000..131252e --- /dev/null +++ b/stubs/livewire-common/routes/auth.php @@ -0,0 +1,31 @@ +group(function () { + Volt::route('register', 'pages.auth.register') + ->name('register'); + + Volt::route('login', 'pages.auth.login') + ->name('login'); + + Volt::route('forgot-password', 'pages.auth.forgot-password') + ->name('password.request'); + + Volt::route('reset-password/{token}', 'pages.auth.reset-password') + ->name('password.reset'); +}); + +Route::middleware('auth')->group(function () { + Volt::route('verify-email', 'pages.auth.verify-email') + ->name('verification.notice'); + + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + + Volt::route('confirm-password', 'pages.auth.confirm-password') + ->name('password.confirm'); +}); diff --git a/stubs/livewire-common/routes/web.php b/stubs/livewire-common/routes/web.php new file mode 100644 index 0000000..a07a282 --- /dev/null +++ b/stubs/livewire-common/routes/web.php @@ -0,0 +1,15 @@ +middleware(['auth', 'verified']) + ->name('dashboard'); + +Route::view('profile', 'profile') + ->middleware(['auth']) + ->name('profile'); + +require __DIR__.'/auth.php'; diff --git a/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php b/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php new file mode 100644 index 0000000..bf77746 --- /dev/null +++ b/stubs/livewire-common/tests/Feature/Auth/AuthenticationTest.php @@ -0,0 +1,86 @@ +get('/login'); + + $response + ->assertOk() + ->assertSeeVolt('pages.auth.login'); + } + + public function test_users_can_authenticate_using_the_login_screen(): void + { + $user = User::factory()->create(); + + $component = Volt::test('pages.auth.login') + ->set('form.email', $user->email) + ->set('form.password', 'password'); + + $component->call('login'); + + $component + ->assertHasNoErrors() + ->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); + } + + public function test_users_can_not_authenticate_with_invalid_password(): void + { + $user = User::factory()->create(); + + $component = Volt::test('pages.auth.login') + ->set('form.email', $user->email) + ->set('form.password', 'wrong-password'); + + $component->call('login'); + + $component + ->assertHasErrors() + ->assertNoRedirect(); + + $this->assertGuest(); + } + + public function test_navigation_menu_can_be_rendered(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $response = $this->get('/dashboard'); + + $response + ->assertOk() + ->assertSeeVolt('layout.navigation'); + } + + public function test_users_can_logout(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('layout.navigation'); + + $component->call('logout'); + + $component + ->assertHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); + } +} diff --git a/stubs/livewire-common/tests/Feature/Auth/EmailVerificationTest.php b/stubs/livewire-common/tests/Feature/Auth/EmailVerificationTest.php new file mode 100644 index 0000000..1236983 --- /dev/null +++ b/stubs/livewire-common/tests/Feature/Auth/EmailVerificationTest.php @@ -0,0 +1,60 @@ +unverified()->create(); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response + ->assertSeeVolt('pages.auth.verify-email') + ->assertStatus(200); + } + + public function test_email_can_be_verified(): void + { + $user = User::factory()->unverified()->create(); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + $this->assertTrue($user->fresh()->hasVerifiedEmail()); + $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); + } + + public function test_email_is_not_verified_with_invalid_hash(): void + { + $user = User::factory()->unverified()->create(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + $this->assertFalse($user->fresh()->hasVerifiedEmail()); + } +} diff --git a/stubs/livewire-common/tests/Feature/Auth/PasswordConfirmationTest.php b/stubs/livewire-common/tests/Feature/Auth/PasswordConfirmationTest.php new file mode 100644 index 0000000..e91c6bb --- /dev/null +++ b/stubs/livewire-common/tests/Feature/Auth/PasswordConfirmationTest.php @@ -0,0 +1,56 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response + ->assertSeeVolt('pages.auth.confirm-password') + ->assertStatus(200); + } + + public function test_password_can_be_confirmed(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('pages.auth.confirm-password') + ->set('password', 'password'); + + $component->call('confirmPassword'); + + $component + ->assertRedirect('/dashboard') + ->assertHasNoErrors(); + } + + public function test_password_is_not_confirmed_with_invalid_password(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('pages.auth.confirm-password') + ->set('password', 'wrong-password'); + + $component->call('confirmPassword'); + + $component + ->assertNoRedirect() + ->assertHasErrors('password'); + } +} diff --git a/stubs/livewire-common/tests/Feature/Auth/PasswordResetTest.php b/stubs/livewire-common/tests/Feature/Auth/PasswordResetTest.php new file mode 100644 index 0000000..f5bc765 --- /dev/null +++ b/stubs/livewire-common/tests/Feature/Auth/PasswordResetTest.php @@ -0,0 +1,84 @@ +get('/forgot-password'); + + $response + ->assertSeeVolt('pages.auth.forgot-password') + ->assertStatus(200); + } + + public function test_reset_password_link_can_be_requested(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class); + } + + public function test_reset_password_screen_can_be_rendered(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response + ->assertSeeVolt('pages.auth.reset-password') + ->assertStatus(200); + + return true; + }); + } + + public function test_password_can_be_reset_with_valid_token(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $component = Volt::test('pages.auth.reset-password', ['token' => $notification->token]) + ->set('email', $user->email) + ->set('password', 'password') + ->set('password_confirmation', 'password'); + + $component->call('resetPassword'); + + $component + ->assertRedirect('/login') + ->assertHasNoErrors(); + + return true; + }); + } +} diff --git a/stubs/livewire-common/tests/Feature/Auth/PasswordUpdateTest.php b/stubs/livewire-common/tests/Feature/Auth/PasswordUpdateTest.php new file mode 100644 index 0000000..d24c295 --- /dev/null +++ b/stubs/livewire-common/tests/Feature/Auth/PasswordUpdateTest.php @@ -0,0 +1,50 @@ +create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-password-form') + ->set('current_password', 'password') + ->set('password', 'new-password') + ->set('password_confirmation', 'new-password') + ->call('updatePassword'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); + } + + public function test_correct_password_must_be_provided_to_update_password(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-password-form') + ->set('current_password', 'wrong-password') + ->set('password', 'new-password') + ->set('password_confirmation', 'new-password') + ->call('updatePassword'); + + $component + ->assertHasErrors(['current_password']) + ->assertNoRedirect(); + } +} diff --git a/stubs/livewire-common/tests/Feature/Auth/RegistrationTest.php b/stubs/livewire-common/tests/Feature/Auth/RegistrationTest.php new file mode 100644 index 0000000..16100aa --- /dev/null +++ b/stubs/livewire-common/tests/Feature/Auth/RegistrationTest.php @@ -0,0 +1,36 @@ +get('/register'); + + $response + ->assertOk() + ->assertSeeVolt('pages.auth.register'); + } + + public function test_new_users_can_register(): void + { + $component = Volt::test('pages.auth.register') + ->set('name', 'Test User') + ->set('email', 'test@example.com') + ->set('password', 'password') + ->set('password_confirmation', 'password'); + + $component->call('register'); + + $component->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); + } +} diff --git a/stubs/livewire-common/tests/Feature/ProfileTest.php b/stubs/livewire-common/tests/Feature/ProfileTest.php new file mode 100644 index 0000000..52fae1b --- /dev/null +++ b/stubs/livewire-common/tests/Feature/ProfileTest.php @@ -0,0 +1,101 @@ +create(); + + $response = $this->actingAs($user)->get('/profile'); + + $response + ->assertOk() + ->assertSeeVolt('profile.update-profile-information-form') + ->assertSeeVolt('profile.update-password-form') + ->assertSeeVolt('profile.delete-user-form'); + } + + public function test_profile_information_can_be_updated(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-profile-information-form') + ->set('name', 'Test User') + ->set('email', 'test@example.com') + ->call('updateProfileInformation'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $user->refresh(); + + $this->assertSame('Test User', $user->name); + $this->assertSame('test@example.com', $user->email); + $this->assertNull($user->email_verified_at); + } + + public function test_email_verification_status_is_unchanged_when_the_email_address_is_unchanged(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-profile-information-form') + ->set('name', 'Test User') + ->set('email', $user->email) + ->call('updateProfileInformation'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $this->assertNotNull($user->refresh()->email_verified_at); + } + + public function test_user_can_delete_their_account(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.delete-user-form') + ->set('password', 'password') + ->call('deleteUser'); + + $component + ->assertHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); + $this->assertNull($user->fresh()); + } + + public function test_correct_password_must_be_provided_to_delete_account(): void + { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.delete-user-form') + ->set('password', 'wrong-password') + ->call('deleteUser'); + + $component + ->assertHasErrors('password') + ->assertNoRedirect(); + + $this->assertNotNull($user->fresh()); + } +} diff --git a/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php b/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php new file mode 100644 index 0000000..8e9c118 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php @@ -0,0 +1,104 @@ +redirect('/', navigate: true); +}; + +?> + + diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php new file mode 100644 index 0000000..e0e74f4 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -0,0 +1,61 @@ + '']); + +rules(['password' => ['required', 'string']]); + +$confirmPassword = function () { + $this->validate(); + + if (! Auth::guard('web')->validate([ + 'email' => Auth::user()->email, + 'password' => $this->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + session(['auth.password_confirmed_at' => time()]); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); +}; + +?> + +
+
+ {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} +
+ +
+ +
+ + + + + +
+ +
+ + {{ __('Confirm') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php new file mode 100644 index 0000000..a94f430 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -0,0 +1,61 @@ + '']); + +rules(['email' => ['required', 'string', 'email']]); + +$sendPasswordResetLink = function () { + $this->validate(); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $this->only('email') + ); + + if ($status != Password::RESET_LINK_SENT) { + $this->addError('email', __($status)); + + return; + } + + $this->reset('email'); + + Session::flash('status', __($status)); +}; + +?> + +
+
+ {{ __('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.') }} +
+ + + + +
+ +
+ + + +
+ +
+ + {{ __('Email Password Reset Link') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php new file mode 100644 index 0000000..6e6c360 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php @@ -0,0 +1,69 @@ +validate(); + + $this->form->authenticate(); + + Session::regenerate(); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); +}; + +?> + +
+ + + +
+ +
+ + + +
+ + +
+ + + + + +
+ + +
+ +
+ +
+ @if (Route::has('password.request')) + + {{ __('Forgot your password?') }} + + @endif + + + {{ __('Log in') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php new file mode 100644 index 0000000..8117db5 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php @@ -0,0 +1,91 @@ + '', + 'email' => '', + 'password' => '', + 'password_confirmation' => '' +]); + +rules([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], +]); + +$register = function () { + $validated = $this->validate(); + + $validated['password'] = Hash::make($validated['password']); + + event(new Registered($user = User::create($validated))); + + Auth::login($user); + + $this->redirect(route('dashboard', absolute: false), navigate: true); +}; + +?> + +
+
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Already registered?') }} + + + + {{ __('Register') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php new file mode 100644 index 0000000..140b33a --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php @@ -0,0 +1,97 @@ +locked(); + +state([ + 'email' => fn () => request()->string('email')->value(), + 'password' => '', + 'password_confirmation' => '' +]); + +rules([ + 'token' => ['required'], + 'email' => ['required', 'string', 'email'], + 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], +]); + +$resetPassword = function () { + $this->validate(); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $this->only('email', 'password', 'password_confirmation', 'token'), + function ($user) { + $user->forceFill([ + 'password' => Hash::make($this->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + if ($status != Password::PASSWORD_RESET) { + $this->addError('email', __($status)); + + return; + } + + Session::flash('status', __($status)); + + $this->redirectRoute('login', navigate: true); +}; + +?> + +
+
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Reset Password') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php new file mode 100644 index 0000000..64708a6 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php @@ -0,0 +1,51 @@ +hasVerifiedEmail()) { + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + + return; + } + + Auth::user()->sendEmailVerificationNotification(); + + Session::flash('status', 'verification-link-sent'); +}; + +$logout = function (Logout $logout) { + $logout(); + + $this->redirect('/', navigate: true); +}; + +?> + +
+
+ {{ __('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.') }} +
+ + @if (session('status') == 'verification-link-sent') +
+ {{ __('A new verification link has been sent to the email address you provided during registration.') }} +
+ @endif + +
+ + {{ __('Resend Verification Email') }} + + + +
+
diff --git a/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php new file mode 100644 index 0000000..2bf0441 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/profile/delete-user-form.blade.php @@ -0,0 +1,76 @@ + '']); + +rules(['password' => ['required', 'string', 'current_password']]); + +$deleteUser = function (Logout $logout) { + $this->validate(); + + tap(Auth::user(), $logout(...))->delete(); + + $this->redirect('/', navigate: true); +}; + +?> + +
+
+

+ {{ __('Delete Account') }} +

+ +

+ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} +

+
+ + {{ __('Delete Account') }} + + +
+ +

+ {{ __('Are you sure you want to delete your account?') }} +

+ +

+ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} +

+ +
+ + + + + +
+ +
+ + {{ __('Cancel') }} + + + + {{ __('Delete Account') }} + +
+
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php new file mode 100644 index 0000000..0e36ba3 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php @@ -0,0 +1,80 @@ + '', + 'password' => '', + 'password_confirmation' => '' +]); + +rules([ + 'current_password' => ['required', 'string', 'current_password'], + 'password' => ['required', 'string', Password::defaults(), 'confirmed'], +]); + +$updatePassword = function () { + try { + $validated = $this->validate(); + } catch (ValidationException $e) { + $this->reset('current_password', 'password', 'password_confirmation'); + + throw $e; + } + + Auth::user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + $this->reset('current_password', 'password', 'password_confirmation'); + + $this->dispatch('password-updated'); +}; + +?> + +
+
+

+ {{ __('Update Password') }} +

+ +

+ {{ __('Ensure your account is using a long, random password to stay secure.') }} +

+
+ +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ {{ __('Save') }} + + + {{ __('Saved.') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php new file mode 100644 index 0000000..3edbaa1 --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/profile/update-profile-information-form.blade.php @@ -0,0 +1,101 @@ + fn () => auth()->user()->name, + 'email' => fn () => auth()->user()->email +]); + +$updateProfileInformation = function () { + $user = Auth::user(); + + $validated = $this->validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', Rule::unique(User::class)->ignore($user->id)], + ]); + + $user->fill($validated); + + if ($user->isDirty('email')) { + $user->email_verified_at = null; + } + + $user->save(); + + $this->dispatch('profile-updated', name: $user->name); +}; + +$sendVerification = function () { + $user = Auth::user(); + + if ($user->hasVerifiedEmail()) { + $this->redirectIntended(default: route('dashboard', absolute: false)); + + return; + } + + $user->sendEmailVerificationNotification(); + + Session::flash('status', 'verification-link-sent'); +}; + +?> + +
+
+

+ {{ __('Profile Information') }} +

+ +

+ {{ __("Update your account's profile information and email address.") }} +

+
+ +
+
+ + + +
+ +
+ + + + + @if (auth()->user() instanceof MustVerifyEmail && ! auth()->user()->hasVerifiedEmail()) +
+

+ {{ __('Your email address is unverified.') }} + + +

+ + @if (session('status') === 'verification-link-sent') +

+ {{ __('A new verification link has been sent to your email address.') }} +

+ @endif +
+ @endif +
+ +
+ {{ __('Save') }} + + + {{ __('Saved.') }} + +
+
+
diff --git a/stubs/livewire-functional/resources/views/livewire/welcome/navigation.blade.php b/stubs/livewire-functional/resources/views/livewire/welcome/navigation.blade.php new file mode 100644 index 0000000..7d079de --- /dev/null +++ b/stubs/livewire-functional/resources/views/livewire/welcome/navigation.blade.php @@ -0,0 +1,26 @@ + diff --git a/stubs/livewire/resources/views/livewire/layout/navigation.blade.php b/stubs/livewire/resources/views/livewire/layout/navigation.blade.php new file mode 100644 index 0000000..6f62113 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/layout/navigation.blade.php @@ -0,0 +1,110 @@ +redirect('/', navigate: true); + } +}; ?> + + diff --git a/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php new file mode 100644 index 0000000..1c1e310 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -0,0 +1,62 @@ +validate([ + 'password' => ['required', 'string'], + ]); + + if (! Auth::guard('web')->validate([ + 'email' => Auth::user()->email, + 'password' => $this->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + session(['auth.password_confirmed_at' => time()]); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + } +}; ?> + +
+
+ {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} +
+ +
+ +
+ + + + + +
+ +
+ + {{ __('Confirm') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php new file mode 100644 index 0000000..b804012 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -0,0 +1,61 @@ +validate([ + 'email' => ['required', 'string', 'email'], + ]); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $this->only('email') + ); + + if ($status != Password::RESET_LINK_SENT) { + $this->addError('email', __($status)); + + return; + } + + $this->reset('email'); + + session()->flash('status', __($status)); + } +}; ?> + +
+
+ {{ __('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.') }} +
+ + + + +
+ +
+ + + +
+ +
+ + {{ __('Email Password Reset Link') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php new file mode 100644 index 0000000..e8b5f94 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php @@ -0,0 +1,71 @@ +validate(); + + $this->form->authenticate(); + + Session::regenerate(); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + } +}; ?> + +
+ + + +
+ +
+ + + +
+ + +
+ + + + + +
+ + +
+ +
+ +
+ @if (Route::has('password.request')) + + {{ __('Forgot your password?') }} + + @endif + + + {{ __('Log in') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php new file mode 100644 index 0000000..59ba2be --- /dev/null +++ b/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php @@ -0,0 +1,88 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], + ]); + + $validated['password'] = Hash::make($validated['password']); + + event(new Registered($user = User::create($validated))); + + Auth::login($user); + + $this->redirect(route('dashboard', absolute: false), navigate: true); + } +}; ?> + +
+
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Already registered?') }} + + + + {{ __('Register') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php new file mode 100644 index 0000000..310a196 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php @@ -0,0 +1,105 @@ +token = $token; + + $this->email = request()->string('email'); + } + + /** + * Reset the password for the given user. + */ + public function resetPassword(): void + { + $this->validate([ + 'token' => ['required'], + 'email' => ['required', 'string', 'email'], + 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], + ]); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $this->only('email', 'password', 'password_confirmation', 'token'), + function ($user) { + $user->forceFill([ + 'password' => Hash::make($this->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + if ($status != Password::PASSWORD_RESET) { + $this->addError('email', __($status)); + + return; + } + + Session::flash('status', __($status)); + + $this->redirectRoute('login', navigate: true); + } +}; ?> + +
+
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Reset Password') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php new file mode 100644 index 0000000..92e87ba --- /dev/null +++ b/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php @@ -0,0 +1,58 @@ +hasVerifiedEmail()) { + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + + return; + } + + Auth::user()->sendEmailVerificationNotification(); + + Session::flash('status', 'verification-link-sent'); + } + + /** + * Log the current user out of the application. + */ + public function logout(Logout $logout): void + { + $logout(); + + $this->redirect('/', navigate: true); + } +}; ?> + +
+
+ {{ __('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.') }} +
+ + @if (session('status') == 'verification-link-sent') +
+ {{ __('A new verification link has been sent to the email address you provided during registration.') }} +
+ @endif + +
+ + {{ __('Resend Verification Email') }} + + + +
+
diff --git a/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php b/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php new file mode 100644 index 0000000..92c9579 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/profile/delete-user-form.blade.php @@ -0,0 +1,79 @@ +validate([ + 'password' => ['required', 'string', 'current_password'], + ]); + + tap(Auth::user(), $logout(...))->delete(); + + $this->redirect('/', navigate: true); + } +}; ?> + +
+
+

+ {{ __('Delete Account') }} +

+ +

+ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} +

+
+ + {{ __('Delete Account') }} + + +
+ +

+ {{ __('Are you sure you want to delete your account?') }} +

+ +

+ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} +

+ +
+ + + + + +
+ +
+ + {{ __('Cancel') }} + + + + {{ __('Delete Account') }} + +
+
+
+
diff --git a/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php b/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php new file mode 100644 index 0000000..568cf2f --- /dev/null +++ b/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php @@ -0,0 +1,79 @@ +validate([ + 'current_password' => ['required', 'string', 'current_password'], + 'password' => ['required', 'string', Password::defaults(), 'confirmed'], + ]); + } catch (ValidationException $e) { + $this->reset('current_password', 'password', 'password_confirmation'); + + throw $e; + } + + Auth::user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + $this->reset('current_password', 'password', 'password_confirmation'); + + $this->dispatch('password-updated'); + } +}; ?> + +
+
+

+ {{ __('Update Password') }} +

+ +

+ {{ __('Ensure your account is using a long, random password to stay secure.') }} +

+
+ +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ {{ __('Save') }} + + + {{ __('Saved.') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php b/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php new file mode 100644 index 0000000..4ff4d86 --- /dev/null +++ b/stubs/livewire/resources/views/livewire/profile/update-profile-information-form.blade.php @@ -0,0 +1,115 @@ +name = Auth::user()->name; + $this->email = Auth::user()->email; + } + + /** + * Update the profile information for the currently authenticated user. + */ + public function updateProfileInformation(): void + { + $user = Auth::user(); + + $validated = $this->validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', Rule::unique(User::class)->ignore($user->id)], + ]); + + $user->fill($validated); + + if ($user->isDirty('email')) { + $user->email_verified_at = null; + } + + $user->save(); + + $this->dispatch('profile-updated', name: $user->name); + } + + /** + * Send an email verification notification to the current user. + */ + public function sendVerification(): void + { + $user = Auth::user(); + + if ($user->hasVerifiedEmail()) { + $this->redirectIntended(default: route('dashboard', absolute: false)); + + return; + } + + $user->sendEmailVerificationNotification(); + + Session::flash('status', 'verification-link-sent'); + } +}; ?> + +
+
+

+ {{ __('Profile Information') }} +

+ +

+ {{ __("Update your account's profile information and email address.") }} +

+
+ +
+
+ + + +
+ +
+ + + + + @if (auth()->user() instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! auth()->user()->hasVerifiedEmail()) +
+

+ {{ __('Your email address is unverified.') }} + + +

+ + @if (session('status') === 'verification-link-sent') +

+ {{ __('A new verification link has been sent to your email address.') }} +

+ @endif +
+ @endif +
+ +
+ {{ __('Save') }} + + + {{ __('Saved.') }} + +
+
+
diff --git a/stubs/livewire/resources/views/livewire/welcome/navigation.blade.php b/stubs/livewire/resources/views/livewire/welcome/navigation.blade.php new file mode 100644 index 0000000..7d079de --- /dev/null +++ b/stubs/livewire/resources/views/livewire/welcome/navigation.blade.php @@ -0,0 +1,26 @@ + From fba510cb05268c2da65205713a5c52e20c79a7f8 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sun, 2 Feb 2025 23:44:42 +0100 Subject: [PATCH 02/16] tests: test only livewire --- .github/workflows/tests.yml | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7ce7cd1..759bb8c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,19 +9,23 @@ jobs: strategy: fail-fast: true matrix: - stack: [blade, react, vue, svelte] - laravel: [11] - args: ["", --pest, --module=Authentication] - include: - - stack: vue - args: --ssr --typescript --dark - laravel: 11 - - stack: react - args: --ssr --typescript --dark - laravel: 11 - - stack: svelte - args: --ssr --typescript --dark - laravel: 11 + # to uncomment later when the module is ready for livewire + stack: [livewire, livewire-functional] + laravel: [ 11 ] + args: [ "", --pest ] + #stack: [blade, react, vue, svelte] + #laravel: [11] + #args: ["", --pest, --module=Authentication] + #include: + #- stack: vue + #args: --ssr --typescript --dark + #laravel: 11 + #- stack: react + #args: --ssr --typescript --dark + #laravel: 11 + #- stack: svelte + #args: --ssr --typescript --dark + #laravel: 11 name: Test Stubs - Laravel ${{ matrix.laravel }} - ${{ matrix.stack }} ${{ matrix.args }} From ce5e098672fee2214c4a13ea3fcae3907cd8ebde Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sun, 2 Feb 2025 23:46:15 +0100 Subject: [PATCH 03/16] missing import --- src/Console/InstallModuleLivewire.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index a275b00..3174c60 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -6,6 +6,7 @@ use Illuminate\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; +use Symfony\Component\Process\Process; trait InstallModuleLivewire { From 1924ef81ce334ee9de293b2f5345742d0bdd0bf6 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sun, 2 Feb 2025 23:49:14 +0100 Subject: [PATCH 04/16] wip --- src/Console/InstallModuleLivewire.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 3174c60..9c0f970 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -38,7 +38,7 @@ protected function installLivewireModule(string $moduleName, $functional): ?int ->run(); // Controllers - $this->copyFileWithNamespace( + $this->copyModuleFilesWithNamespaceLivewire( $moduleName, __DIR__.'/../../stubs/blade-module/app/Http/Controllers/Auth/VerifyEmailController.php', base_path('Modules/'.$moduleName.'/app/Http/Controllers/Auth/VerifyEmailController.php') From 5f657c06c6b0cf121ec43dddda44f739090ef96d Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sun, 2 Feb 2025 23:50:35 +0100 Subject: [PATCH 05/16] wip --- src/Console/InstallModuleLivewire.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 9c0f970..9b2ca02 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -41,7 +41,7 @@ protected function installLivewireModule(string $moduleName, $functional): ?int $this->copyModuleFilesWithNamespaceLivewire( $moduleName, __DIR__.'/../../stubs/blade-module/app/Http/Controllers/Auth/VerifyEmailController.php', - base_path('Modules/'.$moduleName.'/app/Http/Controllers/Auth/VerifyEmailController.php') + base_path('Modules/'.$moduleName.'/app/Http/Controllers/VerifyEmailController.php') ); // Views... From b37e1556103c15985af0d949a3c12e8aef05c5fa Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sun, 2 Feb 2025 23:59:57 +0100 Subject: [PATCH 06/16] refactor copy files --- src/Console/InstallCommand.php | 2 +- src/Console/InstallModuleLivewire.php | 72 ++++++++++++++++++++------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 7098b7e..2b0708a 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -27,7 +27,7 @@ #[AsCommand(name: 'flextra:install')] final class InstallCommand extends Command implements PromptsForMissingInput { - use InstallModuleLivewire, InstallModuleBlade, InstallReactWithInertia, InstallSvelteWithInertia, InstallVueWithInertia; + use InstallModuleBlade, InstallModuleLivewire, InstallReactWithInertia, InstallSvelteWithInertia, InstallVueWithInertia; /** * The console command signature and name. diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 9b2ca02..02c5e8c 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -12,8 +12,6 @@ trait InstallModuleLivewire { /** * Install the Livewire flextra stack. - * - * @return int|null */ protected function installLivewireModule(string $moduleName, $functional): ?int { @@ -143,32 +141,68 @@ protected function installLivewireModule(string $moduleName, $functional): ?int $this->components->info('Flextra Modules '.$moduleName.' Livewire scaffolding installed successfully.'); } + /** + * Helper function to copy files from a stub directory to a target directory. + */ private function copyModuleFilesWithNamespaceLivewire(string $moduleName, string $stubPath, string $targetPath): void { $filesystem = new Filesystem; + + // Ensure the target directory exists $filesystem->ensureDirectoryExists($targetPath); - // Get all files from the stub path, recursively - $files = (new Filesystem)->allFiles($stubPath); + // Check if the stub path is a file + if ($filesystem->isFile($stubPath)) { + // Process a single file + $this->processFile($moduleName, $stubPath, $targetPath, $filesystem); + } else { + // Process all files in the directory recursively + $files = $filesystem->allFiles($stubPath); + foreach ($files as $file) { + $this->processFile($moduleName, $file->getPathname(), $targetPath, $filesystem, $stubPath); + } + } + } - foreach ($files as $file) { - // Get the relative path of the file within the stub directory - $relativePath = $file->getRelativePath(); + /** + * Helper function to process a single file. + */ + private function processFile(string $moduleName, string $filePath, string $targetPath, Filesystem $filesystem, ?string $stubPath = null): void + { + // Calculate the relative path if a stub path is provided + $relativePath = ''; + if ($stubPath) { + $relativePath = $this->getRelativePath($filePath, $stubPath); + } - // Create the target directory structure if it doesn't exist - $targetDir = $targetPath.($relativePath ? '/'.$relativePath : ''); - $filesystem->ensureDirectoryExists($targetDir); + // Create the target directory structure if it doesn't exist + $targetDir = $targetPath.($relativePath ? '/'.$relativePath : ''); + $filesystem->ensureDirectoryExists($targetDir); - // Read and replace placeholders in the file contents - $contents = file_get_contents($file->getPathname()); - $contents = str_replace('{{moduleName}}', $moduleName, $contents); - $contents = str_replace('{{moduleNameLower}}', strtolower($moduleName), $contents); + // Read and replace placeholders in the file contents + $contents = file_get_contents($filePath); + $contents = str_replace(['{{moduleName}}', '{{moduleNameLower}}'], [$moduleName, strtolower($moduleName)], $contents); - // Write the modified contents to the target file, preserving the directory structure - $filesystem->put( - $targetDir.'/'.$file->getFilename(), - $contents - ); + // Write the modified contents to the target file + $filesystem->put( + $targetDir.'/'.basename($filePath), + $contents + ); + } + + /** + * Helper function to calculate the relative path of a file within a directory. + */ + private function getRelativePath(string $filePath, string $directory): string + { + $directory = rtrim($directory, '/\\').DIRECTORY_SEPARATOR; + $filePath = rtrim($filePath, '/\\'); + + // Ensure the file is within the directory + if (str_starts_with($filePath, $directory)) { + return ltrim(substr($filePath, strlen($directory)), '/\\'); } + + return ''; } } From 5c37551bbf14af9c45c1a870d540b87744f0c54b Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Mon, 3 Feb 2025 00:02:14 +0100 Subject: [PATCH 07/16] typo --- src/Console/InstallModuleLivewire.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 02c5e8c..c24fe67 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -52,7 +52,7 @@ protected function installLivewireModule(string $moduleName, $functional): ?int // Livewire Components... $this->copyModuleFilesWithNamespaceLivewire( $moduleName, - __DIR__.'/../../stubs/'($functional ? 'livewire-functional' : 'livewire') + __DIR__.'/../../stubs/'.($functional ? 'livewire-functional' : 'livewire') .'/resources/views/livewire', base_path('Modules/'.$moduleName.'/resources/views/livewire') ); From b5ec85b7130ab06ebbe96a5b786ed593b1751f98 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Mon, 3 Feb 2025 22:11:02 +0100 Subject: [PATCH 08/16] missing dependencies --- src/Console/InstallModuleLivewire.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index c24fe67..7cfba72 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -15,6 +15,8 @@ trait InstallModuleLivewire */ protected function installLivewireModule(string $moduleName, $functional): ?int { + // Install Laravel Modules Package + $this->installModuleDependencies(); // NPM Packages... $this->updateNodePackages(function ($packages) { return [ From c60ed3ee72af9423366bc371a9b365156c0b14c7 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Mon, 3 Feb 2025 22:48:05 +0100 Subject: [PATCH 09/16] update structure --- src/Console/InstallModuleLivewire.php | 75 ++++++++++++++++++--------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 7cfba72..5cc8335 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -38,62 +38,89 @@ protected function installLivewireModule(string $moduleName, $functional): ?int ->run(); // Controllers - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyFileWithNamespace( $moduleName, __DIR__.'/../../stubs/blade-module/app/Http/Controllers/Auth/VerifyEmailController.php', base_path('Modules/'.$moduleName.'/app/Http/Controllers/VerifyEmailController.php') ); - // Views... - $this->copyModuleFilesWithNamespaceLivewire( + // Views Livewire common ... + $this->copyModuleFilesWithNamespace( $moduleName, - __DIR__.'/../../stubs/livewire-common/resources/views', - base_path('Modules/'.$moduleName.'/resources/views') + __DIR__.'/../../stubs/livewire-common/resources/views/layouts', + base_path('Modules/'.$moduleName.'/resources/views/layouts') + ); + $this->copyModuleFilesWithNamespace( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views/components', + base_path('Modules/'.$moduleName.'/resources/views/components') + ); + $this->copyFileWithNamespace( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views/dashboard.blade.php', + base_path('Modules/'.$moduleName.'/resources/views/dashboard.blade.php') + ); + $this->copyFileWithNamespace( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views/profile.blade.php', + base_path('Modules/'.$moduleName.'/resources/views/profile.blade.php') + ); + $this->copyFileWithNamespace( + $moduleName, + __DIR__.'/../../stubs/livewire-common/resources/views/welcome.blade.php', + base_path('Modules/'.$moduleName.'/resources/views/welcome.blade.php') ); // Livewire Components... - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyModuleFilesWithNamespace( $moduleName, __DIR__.'/../../stubs/'.($functional ? 'livewire-functional' : 'livewire') - .'/resources/views/livewire', - base_path('Modules/'.$moduleName.'/resources/views/livewire') + .'/resources/views/livewire/layout', + base_path('Modules/'.$moduleName.'/resources/views/livewire/layout') ); - - // Views Components... - $this->copyModuleFilesWithNamespaceLivewire( + (new Filesystem)->ensureDirectoryExists(base_path('Modules/'.$moduleName.'/resources/views/livewire/pages')); + $this->copyModuleFilesWithNamespace( $moduleName, - __DIR__.'/../../stubs/blade-module/resources/views/components', - base_path('Modules/'.$moduleName.'/resources/views/components') + __DIR__.'/../../stubs/'.($functional ? 'livewire-functional' : 'livewire') + .'/resources/views/livewire/pages/auth', + base_path('Modules/'.$moduleName.'/resources/views/livewire/pages/auth') ); - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyModuleFilesWithNamespace( $moduleName, - __DIR__.'/../../stubs/livewire-common/resources/views/components', - base_path('Modules/'.$moduleName.'/resources/views/components') + __DIR__.'/../../stubs/'.($functional ? 'livewire-functional' : 'livewire') + .'/resources/views/livewire/profile', + base_path('Modules/'.$moduleName.'/resources/views/livewire/profile') + ); + $this->copyModuleFilesWithNamespace( + $moduleName, + __DIR__.'/../../stubs/'.($functional ? 'livewire-functional' : 'livewire') + .'/resources/views/livewire/welcome', + base_path('Modules/'.$moduleName.'/resources/views/livewire/welcome') ); - // Views Layouts... - $this->copyModuleFilesWithNamespaceLivewire( + // Views Components... + $this->copyModuleFilesWithNamespace( $moduleName, - __DIR__.'/../../stubs/livewire-common/resources/views/layouts', - base_path('Modules/'.$moduleName.'/resources/views/layouts') + __DIR__.'/../../stubs/blade-module/resources/views/components', + base_path('Modules/'.$moduleName.'/resources/views/components') ); // Components... - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyModuleFilesWithNamespace( $moduleName, __DIR__.'/../../stubs/blade-module/app/View/Components', base_path('Modules/'.$moduleName.'/app/View/Components') ); // Actions... - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyModuleFilesWithNamespace( $moduleName, __DIR__.'/../../stubs/livewire-common/app/Livewire/Actions', base_path('Modules/'.$moduleName.'/app/Livewire/Actions') ); // Forms... - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyModuleFilesWithNamespace( $moduleName, __DIR__.'/../../stubs/livewire-common/app/Livewire/Forms', base_path('Modules/'.$moduleName.'/app/Livewire/Forms') @@ -115,7 +142,7 @@ protected function installLivewireModule(string $moduleName, $functional): ?int } // Routes... - $this->copyModuleFilesWithNamespaceLivewire( + $this->copyModuleFilesWithNamespace( $moduleName, __DIR__.'/../../stubs/livewire-common/routes', base_path('Modules/'.$moduleName.'/routes') From 0fb43c86dab90f21de2419de1c8618e6cfec0b16 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Mon, 3 Feb 2025 23:12:47 +0100 Subject: [PATCH 10/16] clean module livewire trait --- src/Console/InstallModuleLivewire.php | 65 +-------------------------- 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 5cc8335..1742e64 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -168,70 +168,7 @@ protected function installLivewireModule(string $moduleName, $functional): ?int } $this->components->info('Flextra Modules '.$moduleName.' Livewire scaffolding installed successfully.'); - } - - /** - * Helper function to copy files from a stub directory to a target directory. - */ - private function copyModuleFilesWithNamespaceLivewire(string $moduleName, string $stubPath, string $targetPath): void - { - $filesystem = new Filesystem; - - // Ensure the target directory exists - $filesystem->ensureDirectoryExists($targetPath); - - // Check if the stub path is a file - if ($filesystem->isFile($stubPath)) { - // Process a single file - $this->processFile($moduleName, $stubPath, $targetPath, $filesystem); - } else { - // Process all files in the directory recursively - $files = $filesystem->allFiles($stubPath); - foreach ($files as $file) { - $this->processFile($moduleName, $file->getPathname(), $targetPath, $filesystem, $stubPath); - } - } - } - - /** - * Helper function to process a single file. - */ - private function processFile(string $moduleName, string $filePath, string $targetPath, Filesystem $filesystem, ?string $stubPath = null): void - { - // Calculate the relative path if a stub path is provided - $relativePath = ''; - if ($stubPath) { - $relativePath = $this->getRelativePath($filePath, $stubPath); - } - - // Create the target directory structure if it doesn't exist - $targetDir = $targetPath.($relativePath ? '/'.$relativePath : ''); - $filesystem->ensureDirectoryExists($targetDir); - - // Read and replace placeholders in the file contents - $contents = file_get_contents($filePath); - $contents = str_replace(['{{moduleName}}', '{{moduleNameLower}}'], [$moduleName, strtolower($moduleName)], $contents); - - // Write the modified contents to the target file - $filesystem->put( - $targetDir.'/'.basename($filePath), - $contents - ); - } - - /** - * Helper function to calculate the relative path of a file within a directory. - */ - private function getRelativePath(string $filePath, string $directory): string - { - $directory = rtrim($directory, '/\\').DIRECTORY_SEPARATOR; - $filePath = rtrim($filePath, '/\\'); - - // Ensure the file is within the directory - if (str_starts_with($filePath, $directory)) { - return ltrim(substr($filePath, strlen($directory)), '/\\'); - } - return ''; + return 0; } } From 2f98a61cfb7187185fdda335d114723b624c1b8b Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Wed, 5 Feb 2025 22:31:42 +0100 Subject: [PATCH 11/16] resolve conflict on tests --- .github/workflows/tests.yml | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 759bb8c..d35e045 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,23 +9,19 @@ jobs: strategy: fail-fast: true matrix: - # to uncomment later when the module is ready for livewire - stack: [livewire, livewire-functional] - laravel: [ 11 ] - args: [ "", --pest ] - #stack: [blade, react, vue, svelte] - #laravel: [11] - #args: ["", --pest, --module=Authentication] - #include: - #- stack: vue - #args: --ssr --typescript --dark - #laravel: 11 - #- stack: react - #args: --ssr --typescript --dark - #laravel: 11 - #- stack: svelte - #args: --ssr --typescript --dark - #laravel: 11 + stack: [blade, react, vue, svelte, livewire, livewire-functional] + laravel: [11] + args: ["", --pest, --module=Authentication] + include: + - stack: vue + args: --ssr --typescript --dark + laravel: 11 + - stack: react + args: --ssr --typescript --dark + laravel: 11 + - stack: svelte + args: --ssr --typescript --dark + laravel: 11 name: Test Stubs - Laravel ${{ matrix.laravel }} - ${{ matrix.stack }} ${{ matrix.args }} @@ -56,10 +52,12 @@ jobs: php artisan flextra:install ${{ matrix.stack }} ${{ matrix.args }} - name: Install NPM dependencies + if: matrix.stack != 'api' run: | npm install - name: Compile assets + if: matrix.stack != 'api' run: | npm run build From 4bb5bc17252ec2d34c379a3dda7f0b137b8c0fa6 Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Wed, 5 Feb 2025 23:00:46 +0100 Subject: [PATCH 12/16] update:stubs livewire --- src/Console/InstallModuleLivewire.php | 20 +++++++++++++++++++ .../app/Livewire/Actions/Logout.php | 2 +- .../app/Livewire/Forms/LoginForm.php | 2 +- stubs/livewire-common/routes/auth.php | 2 +- .../livewire/layout/navigation.blade.php | 2 +- .../views/livewire/pages/auth/login.blade.php | 2 +- .../pages/auth/verify-email.blade.php | 2 +- .../profile/delete-user-form.blade.php | 2 +- .../livewire/layout/navigation.blade.php | 2 +- .../views/livewire/pages/auth/login.blade.php | 2 +- .../pages/auth/verify-email.blade.php | 2 +- .../profile/delete-user-form.blade.php | 2 +- 12 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/Console/InstallModuleLivewire.php b/src/Console/InstallModuleLivewire.php index 1742e64..1a2c06b 100644 --- a/src/Console/InstallModuleLivewire.php +++ b/src/Console/InstallModuleLivewire.php @@ -4,6 +4,7 @@ namespace TooInfinity\Flextra\Console; +use Composer\InstalledVersions; use Illuminate\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; use Symfony\Component\Process\Process; @@ -32,11 +33,15 @@ protected function installLivewireModule(string $moduleName, $functional): ?int return 1; } + // Install Volt... (new Process([$this->phpBinary(), 'artisan', 'volt:install'], base_path())) ->setTimeout(null) ->run(); + // Install Livewire Modules... + $this->installModuleLivewireDependencies(); + // Controllers $this->copyFileWithNamespace( $moduleName, @@ -171,4 +176,19 @@ protected function installLivewireModule(string $moduleName, $functional): ?int return 0; } + + /** + * install laravel module livewire dependencies + * + */ + protected function installModuleLivewireDependencies(): void + { + // Check if laravel Modules installed and install it + if (! InstalledVersions::isInstalled('mhmiton/laravel-modules-livewire')) { + $this->runCommands(['composer require mhmiton/laravel-modules-livewire --dev']); + $this->runCommands(['php artisan vendor:publish --tag=modules-livewire-config']); + $this->runCommands(['composer dump-autoload']); + } + + } } diff --git a/stubs/livewire-common/app/Livewire/Actions/Logout.php b/stubs/livewire-common/app/Livewire/Actions/Logout.php index 3ef481d..2db5c6c 100644 --- a/stubs/livewire-common/app/Livewire/Actions/Logout.php +++ b/stubs/livewire-common/app/Livewire/Actions/Logout.php @@ -1,6 +1,6 @@ Date: Fri, 7 Feb 2025 23:25:51 +0100 Subject: [PATCH 13/16] chore: update components stubs --- .../resources/views/dashboard.blade.php | 4 +-- .../resources/views/layouts/guest.blade.php | 2 +- .../resources/views/profile.blade.php | 4 +-- .../livewire/layout/navigation.blade.php | 26 ++++++++-------- .../pages/auth/confirm-password.blade.php | 12 ++++---- .../pages/auth/forgot-password.blade.php | 14 ++++----- .../views/livewire/pages/auth/login.blade.php | 20 ++++++------- .../livewire/pages/auth/register.blade.php | 30 +++++++++---------- .../pages/auth/reset-password.blade.php | 24 +++++++-------- .../pages/auth/verify-email.blade.php | 6 ++-- .../profile/delete-user-form.blade.php | 22 +++++++------- .../profile/update-password-form.blade.php | 24 +++++++-------- .../update-profile-information-form.blade.php | 20 ++++++------- .../livewire/layout/navigation.blade.php | 26 ++++++++-------- .../pages/auth/confirm-password.blade.php | 12 ++++---- .../pages/auth/forgot-password.blade.php | 14 ++++----- .../views/livewire/pages/auth/login.blade.php | 20 ++++++------- .../livewire/pages/auth/register.blade.php | 30 +++++++++---------- .../pages/auth/reset-password.blade.php | 24 +++++++-------- .../pages/auth/verify-email.blade.php | 6 ++-- .../profile/delete-user-form.blade.php | 22 +++++++------- .../profile/update-password-form.blade.php | 24 +++++++-------- .../update-profile-information-form.blade.php | 20 ++++++------- 23 files changed, 203 insertions(+), 203 deletions(-) diff --git a/stubs/livewire-common/resources/views/dashboard.blade.php b/stubs/livewire-common/resources/views/dashboard.blade.php index 4024c64..d21afaa 100644 --- a/stubs/livewire-common/resources/views/dashboard.blade.php +++ b/stubs/livewire-common/resources/views/dashboard.blade.php @@ -1,4 +1,4 @@ - +

{{ __('Dashboard') }} @@ -14,4 +14,4 @@ - + diff --git a/stubs/livewire-common/resources/views/layouts/guest.blade.php b/stubs/livewire-common/resources/views/layouts/guest.blade.php index eaa6065..9e3d023 100644 --- a/stubs/livewire-common/resources/views/layouts/guest.blade.php +++ b/stubs/livewire-common/resources/views/layouts/guest.blade.php @@ -18,7 +18,7 @@
diff --git a/stubs/livewire-common/resources/views/profile.blade.php b/stubs/livewire-common/resources/views/profile.blade.php index b14bcc1..5629a26 100644 --- a/stubs/livewire-common/resources/views/profile.blade.php +++ b/stubs/livewire-common/resources/views/profile.blade.php @@ -1,4 +1,4 @@ - +

{{ __('Profile') }} @@ -26,4 +26,4 @@

- + diff --git a/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php b/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php index 0fe3a17..51faf3c 100644 --- a/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/layout/navigation.blade.php @@ -18,21 +18,21 @@ @@ -75,9 +75,9 @@
- + {{ __('Profile') }} - +
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php index e0e74f4..c9a9604 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -7,7 +7,7 @@ use function Livewire\Volt\rules; use function Livewire\Volt\state; -layout('layouts.guest'); +layout('{{moduleNameLower}}::layouts.guest'); state(['password' => '']); @@ -40,22 +40,22 @@
- + - - +
- + {{ __('Confirm') }} - +
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php index a94f430..fcf865a 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -7,7 +7,7 @@ use function Livewire\Volt\rules; use function Livewire\Volt\state; -layout('layouts.guest'); +layout('{{moduleNameLower}}::layouts.guest'); state(['email' => '']); @@ -42,20 +42,20 @@ - +
- - - + + +
- + {{ __('Email Password Reset Link') }} - +
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php index b0d5e1e..e8ccb0d 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/login.blade.php @@ -6,7 +6,7 @@ use function Livewire\Volt\form; use function Livewire\Volt\layout; -layout('layouts.guest'); +layout('{{moduleNameLower}}::layouts.guest'); form(LoginForm::class); @@ -24,26 +24,26 @@
- +
- - - + + +
- + - - +
@@ -61,9 +61,9 @@ @endif - + {{ __('Log in') }} - +
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php index 8117db5..e0cee0e 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/register.blade.php @@ -10,7 +10,7 @@ use function Livewire\Volt\rules; use function Livewire\Volt\state; -layout('layouts.guest'); +layout('{{moduleNameLower}}::layouts.guest'); state([ 'name' => '', @@ -43,39 +43,39 @@
- - - + + +
- - - + + +
- + - - +
- + - - +
@@ -83,9 +83,9 @@ {{ __('Already registered?') }} - + {{ __('Register') }} - +
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php index 140b33a..1907c5c 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/reset-password.blade.php @@ -11,7 +11,7 @@ use function Livewire\Volt\rules; use function Livewire\Volt\state; -layout('layouts.guest'); +layout('{{moduleNameLower}}::layouts.guest'); state('token')->locked(); @@ -65,33 +65,33 @@ function ($user) {
- - - + + +
- - - + + +
- + - - +
- + {{ __('Reset Password') }} - +
diff --git a/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php b/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php index 30c1920..380a193 100644 --- a/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/pages/auth/verify-email.blade.php @@ -6,7 +6,7 @@ use function Livewire\Volt\layout; -layout('layouts.guest'); +layout('{{moduleNameLower}}::layouts.guest'); $sendVerification = function () { if (Auth::user()->hasVerifiedEmail()) { @@ -40,9 +40,9 @@ @endif
- + {{ __('Resend Verification Email') }} - + - +
@@ -81,9 +81,9 @@ public function logout(Logout $logout): void
- + {{ __('Profile') }} - +
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php index 1c1e310..544802a 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -5,7 +5,7 @@ use Livewire\Attributes\Layout; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new #[Layout('{{moduleNameLower}}::layouts.guest')] class extends Component { public string $password = ''; @@ -41,22 +41,22 @@ public function confirmPassword(): void
- + - - +
- + {{ __('Confirm') }} - +
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php index b804012..1924c3c 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -4,7 +4,7 @@ use Livewire\Attributes\Layout; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new #[Layout('{{moduleNameLower}}::layouts.guest')] class extends Component { public string $email = ''; @@ -42,20 +42,20 @@ public function sendPasswordResetLink(): void - +
- - - + + +
- + {{ __('Email Password Reset Link') }} - +
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php index 88d96e6..afc18c8 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/login.blade.php @@ -5,7 +5,7 @@ use Livewire\Attributes\Layout; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new #[Layout('{{moduleNameLower}}::layouts.guest')] class extends Component { public LoginForm $form; @@ -26,26 +26,26 @@ public function login(): void
- +
- - - + + +
- + - - +
@@ -63,9 +63,9 @@ public function login(): void @endif - + {{ __('Log in') }} - +
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php index 59ba2be..b473fa7 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/register.blade.php @@ -8,7 +8,7 @@ use Livewire\Attributes\Layout; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new #[Layout('{{moduleNameLower}}::layouts.guest')] class extends Component { public string $name = ''; public string $email = ''; @@ -40,39 +40,39 @@ public function register(): void
- - - + + +
- - - + + +
- + - - +
- + - - +
@@ -80,9 +80,9 @@ public function register(): void {{ __('Already registered?') }} - + {{ __('Register') }} - +
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php index 310a196..931c178 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/reset-password.blade.php @@ -10,7 +10,7 @@ use Livewire\Attributes\Locked; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new #[Layout('{{moduleNameLower}}::layouts.guest')] class extends Component { #[Locked] public string $token = ''; @@ -73,33 +73,33 @@ function ($user) {
- - - + + +
- - - + + +
- + - - +
- + {{ __('Reset Password') }} - +
diff --git a/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php b/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php index 1b55fff..3e66883 100644 --- a/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php +++ b/stubs/livewire/resources/views/livewire/pages/auth/verify-email.blade.php @@ -6,7 +6,7 @@ use Livewire\Attributes\Layout; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new #[Layout('{{moduleNameLower}}::layouts.guest')] class extends Component { /** * Send an email verification notification to the user. @@ -47,9 +47,9 @@ public function logout(Logout $logout): void @endif
- + {{ __('Resend Verification Email') }} - + diff --git a/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php b/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php index d92f4e2..207d2d8 100644 --- a/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php +++ b/stubs/livewire-functional/resources/views/livewire/profile/update-password-form.blade.php @@ -65,7 +65,7 @@
- +
diff --git a/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php b/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php index e966865..e65fd17 100644 --- a/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php +++ b/stubs/livewire/resources/views/livewire/profile/update-password-form.blade.php @@ -58,7 +58,7 @@ public function updatePassword(): void
- +
From cd5789fbf5158f0e25edc757d04fe59687de7efd Mon Sep 17 00:00:00 2001 From: TouwfiQ Meghlaoui Date: Sat, 8 Feb 2025 00:03:32 +0100 Subject: [PATCH 16/16] typo --- .../resources/views/livewire/layout/navigation.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/livewire/resources/views/livewire/layout/navigation.blade.php b/stubs/livewire/resources/views/livewire/layout/navigation.blade.php index 5c17719..05f0059 100644 --- a/stubs/livewire/resources/views/livewire/layout/navigation.blade.php +++ b/stubs/livewire/resources/views/livewire/layout/navigation.blade.php @@ -58,9 +58,9 @@ public function logout(Logout $logout): void