diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php
deleted file mode 100644
index 8dec071d..00000000
--- a/app/Http/Controllers/Auth/LoginController.php
+++ /dev/null
@@ -1,60 +0,0 @@
-middleware('guest')->except('logout');
- }
-
- public function showLoginForm(): Response
- {
- session()->put('title', 'Anmelden');
-
- return \Inertia::render('authentication/VLogin');
- }
-
- /**
- * Validate the user login request.
- */
- protected function validateLogin(Request $request): void
- {
- $request->validate([
- $this->username() => 'required|max:255|string|email',
- 'password' => 'required|string',
- ]);
- }
-}
diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php
deleted file mode 100644
index edb9c4a4..00000000
--- a/app/Http/Controllers/Auth/RegisterController.php
+++ /dev/null
@@ -1,71 +0,0 @@
-middleware('guest');
- }
-
- /**
- * Get a validator for an incoming registration request.
- *
- * @return \Illuminate\Contracts\Validation\Validator
- */
- protected function validator(array $data)
- {
- return Validator::make($data, [
- 'name' => ['required', 'string', 'max:255'],
- 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
- 'password' => ['required', 'string', 'min:8', 'confirmed'],
- ]);
- }
-
- /**
- * Create a new user instance after a valid registration.
- *
- * @return \App\User
- */
- protected function create(array $data)
- {
- return User::create([
- 'name' => $data['name'],
- 'email' => $data['email'],
- 'password' => Hash::make($data['password']),
- ]);
- }
-}
diff --git a/app/View/Page/Full.php b/app/View/Page/Full.php
new file mode 100644
index 00000000..27ca702c
--- /dev/null
+++ b/app/View/Page/Full.php
@@ -0,0 +1,32 @@
+put('title', $title);
+ }
+
+ public function render()
+ {
+ return <<<'HTML'
+
+ @if ($heading)
+
+
{{$heading}}
+
+
+ @endif
+
+
+ {{ $slot }}
+
+
+ HTML;
+ }
+}
diff --git a/app/View/Ui/Button.php b/app/View/Ui/Button.php
new file mode 100644
index 00000000..e1f1129e
--- /dev/null
+++ b/app/View/Ui/Button.php
@@ -0,0 +1,22 @@
+
+ {{$slot}}
+
+ HTML;
+ }
+}
diff --git a/config/app.php b/config/app.php
index 175422f4..67ccda3b 100644
--- a/config/app.php
+++ b/config/app.php
@@ -183,6 +183,7 @@ return [
Modules\Invoice\InvoiceServiceProvider::class,
Modules\Mailgateway\MailgatewayServiceProvider::class,
Modules\Nami\NamiServiceProvider::class,
+ Modules\Auth\AuthServiceProvider::class,
],
/*
diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php
index a8bd37bb..fc90d337 100644
--- a/database/factories/UserFactory.php
+++ b/database/factories/UserFactory.php
@@ -27,4 +27,9 @@ class UserFactory extends Factory
'lastname' => $this->faker->lastName,
];
}
+
+ public function loginData(string $email, string $password): self
+ {
+ return $this->state(['email' => $email, 'password' => Hash::make($password)]);
+ }
}
diff --git a/modules/Auth/AuthServiceProvider.php b/modules/Auth/AuthServiceProvider.php
new file mode 100644
index 00000000..85e58e7d
--- /dev/null
+++ b/modules/Auth/AuthServiceProvider.php
@@ -0,0 +1,31 @@
+middleware(['web', 'guest'])->group(function ($router) {
+ $router->get('/login', LoginForm::class)->name('login');
+ });
+ }
+}
diff --git a/modules/Auth/Components/LoginForm.php b/modules/Auth/Components/LoginForm.php
new file mode 100644
index 00000000..3a6d2f8f
--- /dev/null
+++ b/modules/Auth/Components/LoginForm.php
@@ -0,0 +1,62 @@
+validate([
+ 'email' => 'required|max:255|string|email',
+ 'password' => 'required|string',
+ ]);
+ }
+
+ protected function credentials(Request $request)
+ {
+ return ['email' => $this->email, 'password' => $this->password];
+ }
+
+ protected function sendLoginResponse(Request $request)
+ {
+ return redirect()->intended('/');
+ }
+
+ public function submit()
+ {
+ return $this->login(request());
+ }
+
+ #[Layout('components.layouts.full')]
+ public function render(): string
+ {
+ return <<<'HTML'
+
+
+
+ HTML;
+ }
+}
diff --git a/modules/Auth/Components/LoginFormTest.php b/modules/Auth/Components/LoginFormTest.php
new file mode 100644
index 00000000..318598cd
--- /dev/null
+++ b/modules/Auth/Components/LoginFormTest.php
@@ -0,0 +1,69 @@
+get('/')->assertRedirect('/login');
+});
+
+it('displays component', function () {
+ test()->get('/login')->assertSeeLivewire(LoginForm::class)->assertDontSee('Dashboard');
+});
+
+it('displays form', function () {
+ Livewire::test(LoginForm::class)
+ ->assertSee('Login')
+ ->assertSee('Passwort vergessen');
+});
+
+it('loggs in', function () {
+ User::factory()->loginData('admin@example.com', 'secret')->create();
+ Livewire::test(LoginForm::class)
+ ->set('email', 'admin@example.com')
+ ->set('password', 'secret')
+ ->call('submit')
+ ->assertRedirect('/');
+
+ $this->assertEquals('admin@example.com', auth()->user()->email);
+});
+
+it('displays failed login response', function () {
+ Livewire::test(LoginForm::class)
+ ->set('email', 'admin@example.com')
+ ->set('password', 'secret')
+ ->call('submit')
+ ->assertHasErrors(['email' => 'Login fehlgeschlagen.']);
+});
+
+it('increments login attempts', function () {
+ Event::fake([Lockout::class]);
+ Livewire::test(LoginForm::class)
+ ->set('email', 'admin@example.com')
+ ->set('password', 'secret')
+ ->call('submit')
+ ->call('submit')
+ ->call('submit')
+ ->call('submit')
+ ->call('submit')
+ ->call('submit');
+ Event::assertDispatchedTimes(Lockout::class);
+});
+
+it('requires email and password', function () {
+ User::factory()->loginData('admin@example.com', 'secret')->create();
+ Livewire::test(LoginForm::class)
+ ->set('email', '')
+ ->set('password', '')
+ ->call('submit')
+ ->assertHasErrors(['email', 'password']);
+});
diff --git a/resources/js/components/page/FullHeadingBanner.vue b/resources/js/components/page/FullHeadingBanner.vue
deleted file mode 100644
index f686f8bb..00000000
--- a/resources/js/components/page/FullHeadingBanner.vue
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/resources/js/views/VLogin.vue b/resources/js/views/VLogin.vue
deleted file mode 100644
index c65ea511..00000000
--- a/resources/js/views/VLogin.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
- Login
-
-
-
-
-
-
diff --git a/resources/js/views/authentication/VLogin.vue b/resources/js/views/authentication/VLogin.vue
deleted file mode 100644
index 9d4ee1d4..00000000
--- a/resources/js/views/authentication/VLogin.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
- Login
-
-
-
-
-
-
diff --git a/resources/lang/de/auth.php b/resources/lang/de/auth.php
index 24d74c85..cf287535 100644
--- a/resources/lang/de/auth.php
+++ b/resources/lang/de/auth.php
@@ -12,6 +12,6 @@ return [
|
*/
- 'failed' => 'Diese Kombination aus Zugangsdaten wurde nicht in unserer Datenbank gefunden.',
+ 'failed' => 'Login fehlgeschlagen.',
'throttle' => 'Zu viele Loginversuche. Versuchen Sie es bitte in :seconds Sekunden nochmal.',
];
diff --git a/resources/views/components/layouts/full.blade.php b/resources/views/components/layouts/full.blade.php
new file mode 100644
index 00000000..a2102f9d
--- /dev/null
+++ b/resources/views/components/layouts/full.blade.php
@@ -0,0 +1,8 @@
+
+
+
+
+ {{ $slot }}
+ @livewireScriptConfig
+
+
diff --git a/routes/web.php b/routes/web.php
index 21d0ac81..cb4725d3 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -76,9 +76,7 @@ use App\Membership\Actions\MembershipStoreAction;
use App\Membership\Actions\MembershipUpdateAction;
use App\Payment\SubscriptionController;
-Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void {
- Auth::routes(['register' => false]);
-});
+
Route::group(['middleware' => 'auth:web'], function (): void {
Route::post('/nami/login-check', NamiLoginCheckAction::class)->name('nami.login-check');