diff --git a/app/Domains/Webhook/Models/Webhook.php b/app/Domains/Webhook/Models/Webhook.php index f24d182..ac06351 100644 --- a/app/Domains/Webhook/Models/Webhook.php +++ b/app/Domains/Webhook/Models/Webhook.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; + class Webhook extends Model { /** @use HasFactory */ diff --git a/app/Livewire/Modals/ConfirmAction.php b/app/Livewire/Modals/ConfirmAction.php index 13c8908..7d6d432 100644 --- a/app/Livewire/Modals/ConfirmAction.php +++ b/app/Livewire/Modals/ConfirmAction.php @@ -2,16 +2,23 @@ namespace App\Livewire\Modals; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class ConfirmAction extends ModalComponent { public string $title = 'Confirm'; + public string $message = 'Are you sure?'; + public string $confirmLabel = 'Confirm'; + public string $cancelLabel = 'Cancel'; + public string $confirmEvent = ''; + public array $confirmEventParams = []; + public bool $danger = false; public function confirm(): void @@ -28,7 +35,7 @@ class ConfirmAction extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.confirm-action'); } diff --git a/app/Livewire/Modals/CreateApiToken.php b/app/Livewire/Modals/CreateApiToken.php index 0f676f0..f895b1e 100644 --- a/app/Livewire/Modals/CreateApiToken.php +++ b/app/Livewire/Modals/CreateApiToken.php @@ -2,11 +2,13 @@ namespace App\Livewire\Modals; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class CreateApiToken extends ModalComponent { public string $name = ''; + public ?string $plainTextToken = null; protected function rules(): array @@ -31,7 +33,7 @@ class CreateApiToken extends ModalComponent return false; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.create-api-token'); } diff --git a/app/Livewire/Modals/CreateLink.php b/app/Livewire/Modals/CreateLink.php index 3fd88c2..474b517 100644 --- a/app/Livewire/Modals/CreateLink.php +++ b/app/Livewire/Modals/CreateLink.php @@ -4,12 +4,15 @@ namespace App\Livewire\Modals; use App\Domains\Link\Models\Link; use Illuminate\Support\Str; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class CreateLink extends ModalComponent { public string $targetUrl = ''; + public string $slug = ''; + public string $title = ''; protected $rules = [ @@ -46,7 +49,7 @@ class CreateLink extends ModalComponent $this->closeModal(); } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.create-link'); } diff --git a/app/Livewire/Modals/CreateWebhook.php b/app/Livewire/Modals/CreateWebhook.php index c127209..2d7c086 100644 --- a/app/Livewire/Modals/CreateWebhook.php +++ b/app/Livewire/Modals/CreateWebhook.php @@ -4,12 +4,15 @@ namespace App\Livewire\Modals; use App\Domains\Webhook\Models\Webhook; use Illuminate\Support\Str; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class CreateWebhook extends ModalComponent { public string $url = ''; + public string $secret = ''; + public array $selectedEvents = []; public array $availableEvents = [ @@ -25,7 +28,7 @@ class CreateWebhook extends ModalComponent 'url' => 'required|url|max:2048', 'secret' => 'nullable|string|max:255', 'selectedEvents' => 'required|array|min:1', - 'selectedEvents.*' => 'string|in:' . implode(',', array_keys($this->availableEvents)), + 'selectedEvents.*' => 'string|in:'.implode(',', array_keys($this->availableEvents)), ]; } @@ -46,7 +49,7 @@ class CreateWebhook extends ModalComponent $this->closeModal(); } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.create-webhook'); } diff --git a/app/Livewire/Modals/CreateWorkspace.php b/app/Livewire/Modals/CreateWorkspace.php index 7ee99e1..81bdc63 100644 --- a/app/Livewire/Modals/CreateWorkspace.php +++ b/app/Livewire/Modals/CreateWorkspace.php @@ -3,6 +3,7 @@ namespace App\Livewire\Modals; use App\Domains\Workspace\Actions\CreateWorkspace as CreateWorkspaceAction; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class CreateWorkspace extends ModalComponent @@ -31,7 +32,7 @@ class CreateWorkspace extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.create-workspace'); } diff --git a/app/Livewire/Modals/DeleteLink.php b/app/Livewire/Modals/DeleteLink.php index 18b8745..0d3c564 100644 --- a/app/Livewire/Modals/DeleteLink.php +++ b/app/Livewire/Modals/DeleteLink.php @@ -3,11 +3,13 @@ namespace App\Livewire\Modals; use App\Domains\Link\Models\Link; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class DeleteLink extends ModalComponent { public string $linkUlid = ''; + public string $linkTitle = ''; public function mount(string $linkUlid): void @@ -39,7 +41,7 @@ class DeleteLink extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.delete-link'); } diff --git a/app/Livewire/Modals/DeleteWebhook.php b/app/Livewire/Modals/DeleteWebhook.php index 8d48acb..6a1790c 100644 --- a/app/Livewire/Modals/DeleteWebhook.php +++ b/app/Livewire/Modals/DeleteWebhook.php @@ -3,11 +3,13 @@ namespace App\Livewire\Modals; use App\Domains\Webhook\Models\Webhook; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class DeleteWebhook extends ModalComponent { public int $webhookId = 0; + public string $webhookUrl = ''; public function mount(int $webhookId): void @@ -39,7 +41,7 @@ class DeleteWebhook extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.delete-webhook'); } diff --git a/app/Livewire/Modals/DeleteWorkspace.php b/app/Livewire/Modals/DeleteWorkspace.php index 79bf924..273fd8b 100644 --- a/app/Livewire/Modals/DeleteWorkspace.php +++ b/app/Livewire/Modals/DeleteWorkspace.php @@ -3,12 +3,15 @@ namespace App\Livewire\Modals; use App\Domains\Workspace\Models\Workspace; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class DeleteWorkspace extends ModalComponent { public string $workspaceUlid = ''; + public string $workspaceName = ''; + public string $confirmation = ''; public function mount(string $workspaceUlid): void @@ -24,7 +27,7 @@ class DeleteWorkspace extends ModalComponent protected function rules(): array { return [ - 'confirmation' => 'required|in:' . $this->workspaceName, + 'confirmation' => 'required|in:'.$this->workspaceName, ]; } @@ -53,7 +56,7 @@ class DeleteWorkspace extends ModalComponent return false; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.delete-workspace'); } diff --git a/app/Livewire/Modals/EditLink.php b/app/Livewire/Modals/EditLink.php index ed85691..8bad2eb 100644 --- a/app/Livewire/Modals/EditLink.php +++ b/app/Livewire/Modals/EditLink.php @@ -3,15 +3,21 @@ namespace App\Livewire\Modals; use App\Domains\Link\Models\Link; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class EditLink extends ModalComponent { public string $linkUlid = ''; + public int $linkId = 0; + public string $targetUrl = ''; + public string $slug = ''; + public string $title = ''; + public string $status = 'active'; public function mount(string $linkUlid): void @@ -33,7 +39,7 @@ class EditLink extends ModalComponent { return [ 'targetUrl' => 'required|url|max:2048', - 'slug' => 'required|alpha_dash|max:50|unique:links,slug,' . $this->linkId, + 'slug' => 'required|alpha_dash|max:50|unique:links,slug,'.$this->linkId, 'title' => 'nullable|max:200', 'status' => 'required|in:active,inactive', ]; @@ -59,7 +65,7 @@ class EditLink extends ModalComponent $this->closeModal(); } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.edit-link'); } diff --git a/app/Livewire/Modals/EditMemberRole.php b/app/Livewire/Modals/EditMemberRole.php index 9ebaf93..13e8e7a 100644 --- a/app/Livewire/Modals/EditMemberRole.php +++ b/app/Livewire/Modals/EditMemberRole.php @@ -3,12 +3,15 @@ namespace App\Livewire\Modals; use App\Domains\Workspace\Models\WorkspaceMember; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class EditMemberRole extends ModalComponent { public int $memberId = 0; + public string $memberName = ''; + public string $role = 'editor'; protected function rules(): array @@ -50,7 +53,7 @@ class EditMemberRole extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.edit-member-role'); } diff --git a/app/Livewire/Modals/InviteMember.php b/app/Livewire/Modals/InviteMember.php index 2857e01..de0f57f 100644 --- a/app/Livewire/Modals/InviteMember.php +++ b/app/Livewire/Modals/InviteMember.php @@ -4,11 +4,13 @@ namespace App\Livewire\Modals; use App\Domains\Workspace\Models\WorkspaceInvitation; use Illuminate\Support\Str; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class InviteMember extends ModalComponent { public string $email = ''; + public string $role = 'editor'; protected function rules(): array @@ -42,7 +44,7 @@ class InviteMember extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.invite-member'); } diff --git a/app/Livewire/Modals/RemoveMember.php b/app/Livewire/Modals/RemoveMember.php index 20da9f3..bb04b09 100644 --- a/app/Livewire/Modals/RemoveMember.php +++ b/app/Livewire/Modals/RemoveMember.php @@ -3,11 +3,13 @@ namespace App\Livewire\Modals; use App\Domains\Workspace\Models\WorkspaceMember; +use Illuminate\View\View; use LivewireUI\Modal\ModalComponent; class RemoveMember extends ModalComponent { public int $memberId = 0; + public string $memberName = ''; public function mount(int $memberId): void @@ -39,7 +41,7 @@ class RemoveMember extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.remove-member'); } diff --git a/app/Livewire/Modals/RevokeApiToken.php b/app/Livewire/Modals/RevokeApiToken.php index e8664a4..52db7bd 100644 --- a/app/Livewire/Modals/RevokeApiToken.php +++ b/app/Livewire/Modals/RevokeApiToken.php @@ -2,12 +2,14 @@ namespace App\Livewire\Modals; +use Illuminate\View\View; use Laravel\Sanctum\PersonalAccessToken; use LivewireUI\Modal\ModalComponent; class RevokeApiToken extends ModalComponent { public int $tokenId = 0; + public string $tokenName = ''; public function mount(int $tokenId): void @@ -38,7 +40,7 @@ class RevokeApiToken extends ModalComponent return 'sm'; } - public function render(): \Illuminate\View\View + public function render(): View { return view('livewire.modals.revoke-api-token'); } diff --git a/app/Livewire/Pages/Profile/PersonalInfo.php b/app/Livewire/Pages/Profile/PersonalInfo.php index 4be3670..81929bd 100644 --- a/app/Livewire/Pages/Profile/PersonalInfo.php +++ b/app/Livewire/Pages/Profile/PersonalInfo.php @@ -8,6 +8,7 @@ use Livewire\Component; class PersonalInfo extends Component { public string $name = ''; + public string $email = ''; public function mount(): void @@ -20,7 +21,7 @@ class PersonalInfo extends Component { $this->validate([ 'name' => 'required|string|max:100', - 'email' => 'required|email|max:200|unique:users,email,' . auth()->id(), + 'email' => 'required|email|max:200|unique:users,email,'.auth()->id(), ]); auth()->user()->update([ diff --git a/app/Livewire/Pages/Profile/Preferences.php b/app/Livewire/Pages/Profile/Preferences.php index 1c0cfdb..23058aa 100644 --- a/app/Livewire/Pages/Profile/Preferences.php +++ b/app/Livewire/Pages/Profile/Preferences.php @@ -8,6 +8,7 @@ use Livewire\Component; class Preferences extends Component { public string $locale = 'de'; + public string $theme = 'dark'; public function mount(): void diff --git a/app/Livewire/Pages/Profile/Security.php b/app/Livewire/Pages/Profile/Security.php index 1ebfa26..a71ebf0 100644 --- a/app/Livewire/Pages/Profile/Security.php +++ b/app/Livewire/Pages/Profile/Security.php @@ -2,14 +2,16 @@ namespace App\Livewire\Pages\Profile; +use Illuminate\Support\Facades\Hash; use Illuminate\View\View; use Livewire\Component; -use Illuminate\Support\Facades\Hash; class Security extends Component { public string $currentPassword = ''; + public string $newPassword = ''; + public string $newPasswordConfirmation = ''; public function changePassword(): void @@ -19,8 +21,9 @@ class Security extends Component 'newPassword' => 'required|min:8|confirmed', ]); - if (!Hash::check($this->currentPassword, auth()->user()->password)) { + if (! Hash::check($this->currentPassword, auth()->user()->password)) { $this->addError('currentPassword', 'Current password is incorrect.'); + return; } diff --git a/app/Livewire/Pages/Settings/ApiTokens.php b/app/Livewire/Pages/Settings/ApiTokens.php index bf8c287..0efd680 100644 --- a/app/Livewire/Pages/Settings/ApiTokens.php +++ b/app/Livewire/Pages/Settings/ApiTokens.php @@ -4,11 +4,11 @@ namespace App\Livewire\Pages\Settings; use Illuminate\View\View; use Livewire\Component; -use Laravel\Sanctum\PersonalAccessToken; class ApiTokens extends Component { public string $tokenName = ''; + public ?string $newToken = null; public function createToken(): void diff --git a/app/Livewire/Pages/Settings/Index.php b/app/Livewire/Pages/Settings/Index.php index b517699..abef19f 100644 --- a/app/Livewire/Pages/Settings/Index.php +++ b/app/Livewire/Pages/Settings/Index.php @@ -8,6 +8,7 @@ use Livewire\Component; class Index extends Component { public string $workspaceName = ''; + public string $workspaceSlug = ''; public function mount(): void diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 232b6cb..fa81cc8 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; diff --git a/database/seeders/DemoDataSeeder.php b/database/seeders/DemoDataSeeder.php index 0035418..f380b8c 100644 --- a/database/seeders/DemoDataSeeder.php +++ b/database/seeders/DemoDataSeeder.php @@ -8,7 +8,6 @@ use App\Domains\Workspace\Models\Workspace; use App\Domains\Workspace\Models\WorkspaceMember; use App\Models\User; use Illuminate\Database\Seeder; -use Illuminate\Support\Carbon; use Illuminate\Support\Str; class DemoDataSeeder extends Seeder @@ -52,7 +51,7 @@ class DemoDataSeeder extends Seeder $links[] = $link; } - $this->command->info('Created ' . count($links) . ' links'); + $this->command->info('Created '.count($links).' links'); $countries = ['DE', 'US', 'GB', 'FR', 'AT', 'CH', 'NL', 'PL', 'IT', 'ES']; $devices = ['desktop', 'mobile', 'tablet']; diff --git a/routes/auth.php b/routes/auth.php index ead74d9..3daab98 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -24,6 +24,7 @@ Route::post('logout', function (Request $request, Logout $logout) { $logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); + return redirect('/'); })->middleware('auth')->name('logout'); diff --git a/routes/web.php b/routes/web.php index 895017d..77fe202 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,7 +3,14 @@ use App\Http\Controllers\BioPageController; use App\Http\Controllers\RedirectController; use App\Http\Middleware\ResolveWorkspace; +use App\Livewire\Pages\Dashboard\Overview; use App\Livewire\Pages\Links\Index; +use App\Livewire\Pages\Profile\PersonalInfo; +use App\Livewire\Pages\Profile\Preferences; +use App\Livewire\Pages\Profile\Security; +use App\Livewire\Pages\Settings\ApiTokens; +use App\Livewire\Pages\Settings\Webhooks; +use App\Livewire\Pages\Team\Members; use Illuminate\Support\Facades\Route; Route::get('/health', function () { @@ -19,15 +26,16 @@ Route::get('dashboard', function () { if ($workspace) { return redirect()->route('w.dashboard', $workspace->ulid); } + return view('dashboard'); })->middleware(['auth', 'verified'])->name('dashboard'); -Route::get('profile', fn() => redirect()->route('profile.personal'))->middleware(['auth'])->name('profile'); +Route::get('profile', fn () => redirect()->route('profile.personal'))->middleware(['auth'])->name('profile'); Route::middleware(['auth'])->group(function () { - Route::get('/profile/personal', App\Livewire\Pages\Profile\PersonalInfo::class)->name('profile.personal'); - Route::get('/profile/preferences', App\Livewire\Pages\Profile\Preferences::class)->name('profile.preferences'); - Route::get('/profile/security', App\Livewire\Pages\Profile\Security::class)->name('profile.security'); + Route::get('/profile/personal', PersonalInfo::class)->name('profile.personal'); + Route::get('/profile/preferences', Preferences::class)->name('profile.preferences'); + Route::get('/profile/security', Security::class)->name('profile.security'); }); // Workspace-scoped routes — placed before slug catch-all @@ -35,17 +43,17 @@ Route::middleware(['auth', 'verified', ResolveWorkspace::class]) ->prefix('w/{workspace}') ->name('w.') ->group(function () { - Route::get('/', App\Livewire\Pages\Dashboard\Overview::class)->name('dashboard'); + Route::get('/', Overview::class)->name('dashboard'); Route::get('/links', Index::class)->name('links.index'); Route::get('/qr', App\Livewire\Pages\QrCodes\Index::class)->name('qr.index'); Route::get('/bio', App\Livewire\Pages\Bio\Index::class)->name('bio.index'); Route::get('/analytics', App\Livewire\Pages\Analytics\Index::class)->name('analytics.index'); Route::get('/domains', App\Livewire\Pages\Domains\Index::class)->name('domains.index'); Route::get('/settings', App\Livewire\Pages\Settings\Index::class)->name('settings.index'); - Route::get('/settings/api-tokens', App\Livewire\Pages\Settings\ApiTokens::class)->name('settings.api-tokens'); - Route::get('/settings/webhooks', App\Livewire\Pages\Settings\Webhooks::class)->name('settings.webhooks'); + Route::get('/settings/api-tokens', ApiTokens::class)->name('settings.api-tokens'); + Route::get('/settings/webhooks', Webhooks::class)->name('settings.webhooks'); Route::get('/billing', App\Livewire\Pages\Billing\Index::class)->name('billing.index'); - Route::get('/team', App\Livewire\Pages\Team\Members::class)->name('team.index'); + Route::get('/team', Members::class)->name('team.index'); }); // Bio page route — must be before the slug catch-all diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php index 73d2250..184351c 100644 --- a/tests/Feature/Auth/AuthenticationTest.php +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -1,8 +1,8 @@ 'owner', 'joined_at' => now(), ]); + return [$user, $ws]; } diff --git a/tests/Feature/Livewire/WorkspaceModalsTest.php b/tests/Feature/Livewire/WorkspaceModalsTest.php index 50e6a0a..1bac908 100644 --- a/tests/Feature/Livewire/WorkspaceModalsTest.php +++ b/tests/Feature/Livewire/WorkspaceModalsTest.php @@ -16,6 +16,7 @@ use App\Livewire\Modals\RemoveMember; use App\Livewire\Modals\RevokeApiToken; use App\Models\User; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Laravel\Sanctum\PersonalAccessToken; use Livewire\Livewire; function makeWsUser(): array @@ -28,6 +29,7 @@ function makeWsUser(): array 'role' => 'owner', 'joined_at' => now(), ]); + return [$user, $ws]; } @@ -183,7 +185,7 @@ it('revokes token and dispatches apiTokenRevoked', function () { ->call('revoke') ->assertDispatched('apiTokenRevoked'); - expect(\Laravel\Sanctum\PersonalAccessToken::find($pat->id))->toBeNull(); + expect(PersonalAccessToken::find($pat->id))->toBeNull(); }); // CreateWebhook diff --git a/tests/Feature/Livewire/WorkspacePagesTest.php b/tests/Feature/Livewire/WorkspacePagesTest.php index e23d876..6a7d27e 100644 --- a/tests/Feature/Livewire/WorkspacePagesTest.php +++ b/tests/Feature/Livewire/WorkspacePagesTest.php @@ -1,8 +1,8 @@ 'owner', 'joined_at' => now(), ]); + return [$user, $ws]; }