From 61718fa867e0ea5116e32c9ace93564097500447 Mon Sep 17 00:00:00 2001 From: boban Date: Sat, 16 May 2026 07:59:42 +0200 Subject: [PATCH] feat(ui): links index page with search, filter, delete; workspace route group Add Links Livewire page (Index component + blade view) with live search, status filter, paginated table, and delete action using DeleteLink for cache invalidation. Register workspace-scoped route group under w/{workspace} with ResolveWorkspace middleware. Add stub Livewire pages for QrCodes, Bio, Analytics, Domains, Settings, and Billing with a shared coming-soon view. Co-Authored-By: Claude Sonnet 4.6 --- app/Livewire/Pages/Analytics/Index.php | 14 ++++ app/Livewire/Pages/Billing/Index.php | 14 ++++ app/Livewire/Pages/Bio/Index.php | 14 ++++ app/Livewire/Pages/Domains/Index.php | 14 ++++ app/Livewire/Pages/Links/Index.php | 56 ++++++++++++++++ app/Livewire/Pages/QrCodes/Index.php | 14 ++++ app/Livewire/Pages/Settings/Index.php | 14 ++++ .../livewire/pages/coming-soon.blade.php | 1 + .../livewire/pages/links/index.blade.php | 66 +++++++++++++++++++ routes/web.php | 14 ++++ 10 files changed, 221 insertions(+) create mode 100644 app/Livewire/Pages/Analytics/Index.php create mode 100644 app/Livewire/Pages/Billing/Index.php create mode 100644 app/Livewire/Pages/Bio/Index.php create mode 100644 app/Livewire/Pages/Domains/Index.php create mode 100644 app/Livewire/Pages/Links/Index.php create mode 100644 app/Livewire/Pages/QrCodes/Index.php create mode 100644 app/Livewire/Pages/Settings/Index.php create mode 100644 resources/views/livewire/pages/coming-soon.blade.php create mode 100644 resources/views/livewire/pages/links/index.blade.php diff --git a/app/Livewire/Pages/Analytics/Index.php b/app/Livewire/Pages/Analytics/Index.php new file mode 100644 index 0000000..959cd60 --- /dev/null +++ b/app/Livewire/Pages/Analytics/Index.php @@ -0,0 +1,14 @@ +layout('layouts.nimuli-app', ['title' => 'Analytics']); + } +} diff --git a/app/Livewire/Pages/Billing/Index.php b/app/Livewire/Pages/Billing/Index.php new file mode 100644 index 0000000..868f11e --- /dev/null +++ b/app/Livewire/Pages/Billing/Index.php @@ -0,0 +1,14 @@ +layout('layouts.nimuli-app', ['title' => 'Billing']); + } +} diff --git a/app/Livewire/Pages/Bio/Index.php b/app/Livewire/Pages/Bio/Index.php new file mode 100644 index 0000000..f1f3a78 --- /dev/null +++ b/app/Livewire/Pages/Bio/Index.php @@ -0,0 +1,14 @@ +layout('layouts.nimuli-app', ['title' => 'Bio Pages']); + } +} diff --git a/app/Livewire/Pages/Domains/Index.php b/app/Livewire/Pages/Domains/Index.php new file mode 100644 index 0000000..e0e7c80 --- /dev/null +++ b/app/Livewire/Pages/Domains/Index.php @@ -0,0 +1,14 @@ +layout('layouts.nimuli-app', ['title' => 'Domains']); + } +} diff --git a/app/Livewire/Pages/Links/Index.php b/app/Livewire/Pages/Links/Index.php new file mode 100644 index 0000000..63ac65c --- /dev/null +++ b/app/Livewire/Pages/Links/Index.php @@ -0,0 +1,56 @@ +resetPage(); + } + + public function updatingStatusFilter(): void + { + $this->resetPage(); + } + + public function deleteLink(int $id, DeleteLink $action): void + { + $workspace = app('current_workspace'); + $link = Link::where('id', $id) + ->where('workspace_id', $workspace->id) + ->firstOrFail(); + + $this->authorize('update', $workspace); + $action->handle($link); + $this->resetPage(); + } + + public function render(): \Illuminate\View\View + { + $workspace = app('current_workspace'); + + $links = Link::where('workspace_id', $workspace->id) + ->when($this->search, fn($q) => $q->where(function ($q) { + $q->where('title', 'like', "%{$this->search}%") + ->orWhere('slug', 'like', "%{$this->search}%") + ->orWhere('target_url', 'like', "%{$this->search}%"); + })) + ->when($this->statusFilter, fn($q) => $q->where('status', $this->statusFilter)) + ->latest() + ->paginate(20); + + return view('livewire.pages.links.index', compact('links')) + ->layout('layouts.nimuli-app', ['title' => 'Links']); + } +} diff --git a/app/Livewire/Pages/QrCodes/Index.php b/app/Livewire/Pages/QrCodes/Index.php new file mode 100644 index 0000000..9440f04 --- /dev/null +++ b/app/Livewire/Pages/QrCodes/Index.php @@ -0,0 +1,14 @@ +layout('layouts.nimuli-app', ['title' => 'QR Codes']); + } +} diff --git a/app/Livewire/Pages/Settings/Index.php b/app/Livewire/Pages/Settings/Index.php new file mode 100644 index 0000000..022a7d7 --- /dev/null +++ b/app/Livewire/Pages/Settings/Index.php @@ -0,0 +1,14 @@ +layout('layouts.nimuli-app', ['title' => 'Settings']); + } +} diff --git a/resources/views/livewire/pages/coming-soon.blade.php b/resources/views/livewire/pages/coming-soon.blade.php new file mode 100644 index 0000000..aab4a8a --- /dev/null +++ b/resources/views/livewire/pages/coming-soon.blade.php @@ -0,0 +1 @@ +
Coming soon...
diff --git a/resources/views/livewire/pages/links/index.blade.php b/resources/views/livewire/pages/links/index.blade.php new file mode 100644 index 0000000..51bbee1 --- /dev/null +++ b/resources/views/livewire/pages/links/index.blade.php @@ -0,0 +1,66 @@ +
+
+

Links

+ + + New Link + +
+ +
+ + +
+ +
+ + + + + + + + + + + + @forelse($links as $link) + + + + + + + + @empty + + + + @endforelse + +
LinkTargetClicksStatus
+
{{ $link->slug }}
+ @if($link->title) +
{{ $link->title }}
+ @endif +
{{ $link->target_url }}0 + + {{ $link->status }} + + + +
No links yet. Create your first link.
+
+ +
{{ $links->links() }}
+
diff --git a/routes/web.php b/routes/web.php index 70f0a4d..7b7d696 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,20 @@ Route::view('profile', 'profile') ->middleware(['auth']) ->name('profile'); +// Workspace-scoped routes — placed before slug catch-all +Route::middleware(['auth', 'verified', \App\Http\Middleware\ResolveWorkspace::class]) + ->prefix('w/{workspace}') + ->name('w.') + ->group(function () { + Route::get('/links', \App\Livewire\Pages\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('/billing', \App\Livewire\Pages\Billing\Index::class)->name('billing.index'); + }); + // Redirect service — handles slugs on main domain and custom domains // Placed after all named routes so dashboard/profile etc. are not caught here Route::get('/{slug}', \App\Http\Controllers\RedirectController::class)