From 28fbcba2275751be437a9fb742db1d391292e95e Mon Sep 17 00:00:00 2001 From: boban Date: Sat, 16 May 2026 17:30:44 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20bugs=201,4,5,7,8,9=20=E2=80=94=20sidebar?= =?UTF-8?q?=20icon,=20theme=20button,=20copy-link,=20UTM=20computed,=20wor?= =?UTF-8?q?kspace=20context,=20theme=20sync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1: sidebar toggle — single rotating SVG → two x-show SVGs (chevron-left / chevron-right) Bug 4: theme button — template x-if → svg x-show x-cloak; apply() now resolves 'system' to dark/light before setting data-theme (was setting data-theme="system" → CSS ignored it) Bug 5: x-ui.copy-button added to links, bio, qr-codes rows and api-tokens new-token display Bug 7: UtmBuilder::getFullUrlProperty() (Livewire v2 magic) → #[Computed] fullUrl() (Livewire v3 dropped getXProperty magic — $fullUrl was undefined in blade) Bug 8: require_workspace() moved from render() to mount() in Overview, Links/Index, Settings/Index (Livewire update requests hit /livewire/update — ResolveWorkspace middleware never ran → threw) Bug 9: Preferences::save() dispatches theme-changed-server → layout listener calls Alpine.store('theme').apply(theme) so DOM updates immediately without reload Bugs 2, 3, 6 already correct in codebase (layout had transition/ml-60 binding, mobile sidebar used $store.sidebar.open, routes used ULID via model route key). Co-Authored-By: Claude Sonnet 4.6 --- app/Livewire/Modals/UtmBuilder.php | 5 ++-- app/Livewire/Pages/Dashboard/Overview.php | 25 ++++++++++++----- app/Livewire/Pages/Links/Index.php | 11 ++++++-- app/Livewire/Pages/Profile/Preferences.php | 1 + app/Livewire/Pages/Settings/Index.php | 10 +++++-- resources/js/bootstrap.js | 7 +++-- resources/views/components/sidebar.blade.php | 7 +++-- resources/views/components/topbar.blade.php | 28 ++++++++----------- resources/views/layouts/nimuli-app.blade.php | 10 +++++++ .../views/livewire/pages/bio/index.blade.php | 1 + .../pages/dashboard/overview.blade.php | 8 +++--- .../livewire/pages/links/index.blade.php | 1 + .../livewire/pages/qr-codes/index.blade.php | 4 +++ .../pages/settings/api-tokens.blade.php | 3 ++ 14 files changed, 81 insertions(+), 40 deletions(-) diff --git a/app/Livewire/Modals/UtmBuilder.php b/app/Livewire/Modals/UtmBuilder.php index 140f34f..0380404 100644 --- a/app/Livewire/Modals/UtmBuilder.php +++ b/app/Livewire/Modals/UtmBuilder.php @@ -3,9 +3,9 @@ namespace App\Livewire\Modals; use Illuminate\View\View; +use Livewire\Attributes\Computed; use LivewireUI\Modal\ModalComponent; -/** @property-read string $fullUrl */ class UtmBuilder extends ModalComponent { public string $baseUrl = ''; @@ -22,7 +22,8 @@ class UtmBuilder extends ModalComponent public bool $copied = false; - public function getFullUrlProperty(): string + #[Computed] + public function fullUrl(): string { if (! $this->baseUrl) { return ''; diff --git a/app/Livewire/Pages/Dashboard/Overview.php b/app/Livewire/Pages/Dashboard/Overview.php index d28340b..11a83c3 100644 --- a/app/Livewire/Pages/Dashboard/Overview.php +++ b/app/Livewire/Pages/Dashboard/Overview.php @@ -9,27 +9,38 @@ use Livewire\Component; class Overview extends Component { - public function render(): View + public int $workspaceId = 0; + + public string $workspaceUlid = ''; + + public function mount(): void { $workspace = require_workspace(); + $this->workspaceId = $workspace->id; + $this->workspaceUlid = $workspace->ulid; + } + + public function render(): View + { + $wsId = $this->workspaceId; // Hero metrics - $today = Click::where('workspace_id', $workspace->id) + $today = Click::where('workspace_id', $wsId) ->whereDate('clicked_at', today()) ->count(); - $last7 = Click::where('workspace_id', $workspace->id) + $last7 = Click::where('workspace_id', $wsId) ->where('clicked_at', '>=', now()->subDays(7)) ->count(); - $last30 = Click::where('workspace_id', $workspace->id) + $last30 = Click::where('workspace_id', $wsId) ->where('clicked_at', '>=', now()->subDays(30)) ->count(); - $totalLinks = Link::where('workspace_id', $workspace->id)->count(); + $totalLinks = Link::where('workspace_id', $wsId)->count(); // Chart data: daily clicks for last 30 days - $chartData = Click::where('workspace_id', $workspace->id) + $chartData = Click::where('workspace_id', $wsId) ->where('clicked_at', '>=', now()->subDays(29)->startOfDay()) ->selectRaw('DATE(clicked_at) as date, COUNT(*) as total') ->groupBy('date') @@ -45,7 +56,7 @@ class Overview extends Component } // Top links by clicks - $topLinks = Link::where('workspace_id', $workspace->id) + $topLinks = Link::where('workspace_id', $wsId) ->withCount(['clicks as total_clicks']) ->orderByDesc('total_clicks') ->limit(5) diff --git a/app/Livewire/Pages/Links/Index.php b/app/Livewire/Pages/Links/Index.php index f7812cc..17b51d2 100644 --- a/app/Livewire/Pages/Links/Index.php +++ b/app/Livewire/Pages/Links/Index.php @@ -16,6 +16,13 @@ class Index extends Component public string $statusFilter = ''; + public int $workspaceId = 0; + + public function mount(): void + { + $this->workspaceId = require_workspace()->id; + } + public function updatingSearch(): void { $this->resetPage(); @@ -36,9 +43,7 @@ class Index extends Component public function render(): View { - $workspace = require_workspace(); - - $links = Link::where('workspace_id', $workspace->id) + $links = Link::where('workspace_id', $this->workspaceId) ->when($this->search, fn ($q) => $q->where(function ($q) { $q->where('title', 'like', "%{$this->search}%") ->orWhere('slug', 'like', "%{$this->search}%") diff --git a/app/Livewire/Pages/Profile/Preferences.php b/app/Livewire/Pages/Profile/Preferences.php index 23058aa..411f675 100644 --- a/app/Livewire/Pages/Profile/Preferences.php +++ b/app/Livewire/Pages/Profile/Preferences.php @@ -29,6 +29,7 @@ class Preferences extends Component 'theme' => $this->theme, ]); + $this->dispatch('theme-changed-server', theme: $this->theme); session()->flash('status', 'Preferences saved.'); } diff --git a/app/Livewire/Pages/Settings/Index.php b/app/Livewire/Pages/Settings/Index.php index 2bbfca8..4f6fe87 100644 --- a/app/Livewire/Pages/Settings/Index.php +++ b/app/Livewire/Pages/Settings/Index.php @@ -11,9 +11,12 @@ class Index extends Component public string $workspaceSlug = ''; + public int $workspaceId = 0; + public function mount(): void { - $ws = current_workspace(); + $ws = require_workspace(); + $this->workspaceId = $ws->id; $this->workspaceName = $ws->name; $this->workspaceSlug = $ws->slug; } @@ -25,9 +28,10 @@ class Index extends Component 'workspaceSlug' => 'nullable|string|max:50|alpha_dash', ]); - require_workspace()->update([ + $ws = \App\Domains\Workspace\Models\Workspace::findOrFail($this->workspaceId); + $ws->update([ 'name' => $this->workspaceName, - 'slug' => $this->workspaceSlug ?: require_workspace()->slug, + 'slug' => $this->workspaceSlug ?: $ws->slug, ]); session()->flash('status', 'Workspace settings saved.'); diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js index 2918e7b..09554c9 100644 --- a/resources/js/bootstrap.js +++ b/resources/js/bootstrap.js @@ -46,9 +46,12 @@ document.addEventListener('alpine:init', () => { init() { this.apply(this.current); }, apply(theme) { this.current = theme; - document.documentElement.dataset.theme = theme; + const actual = theme === 'system' + ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') + : theme; + document.documentElement.dataset.theme = actual; localStorage.setItem('nimuli_theme', theme); - document.cookie = 'nimuli_theme=' + theme + '; path=/; max-age=31536000; SameSite=Lax'; + document.cookie = 'nimuli_theme=' + actual + '; path=/; max-age=31536000; SameSite=Lax'; }, cycle() { const order = ['dark', 'light', 'system']; diff --git a/resources/views/components/sidebar.blade.php b/resources/views/components/sidebar.blade.php index 1f189c4..30acf33 100644 --- a/resources/views/components/sidebar.blade.php +++ b/resources/views/components/sidebar.blade.php @@ -244,10 +244,13 @@ :title="$store.sidebar.collapsed ? 'Expand sidebar' : 'Collapse sidebar'" class="flex items-center gap-3 w-full px-3 py-2 rounded-lg text-t3 hover:text-t1 hover:bg-s2 transition-colors" > - - + + + + + Collapse diff --git a/resources/views/components/topbar.blade.php b/resources/views/components/topbar.blade.php index 3068898..c3d687c 100644 --- a/resources/views/components/topbar.blade.php +++ b/resources/views/components/topbar.blade.php @@ -36,23 +36,17 @@ :title="'Theme: ' + $store.theme.current" aria-label="Toggle theme" > - - - + + + + + + + + + + + diff --git a/resources/views/layouts/nimuli-app.blade.php b/resources/views/layouts/nimuli-app.blade.php index b1e2e59..3dee09d 100644 --- a/resources/views/layouts/nimuli-app.blade.php +++ b/resources/views/layouts/nimuli-app.blade.php @@ -165,6 +165,16 @@ document.addEventListener('DOMContentLoaded', updateActiveNav); document.addEventListener('livewire:navigated', updateActiveNav); + // Sync server-saved theme to Alpine store + DOM + document.addEventListener('livewire:init', function () { + Livewire.on('theme-changed-server', function (event) { + var theme = event.theme ?? (Array.isArray(event) ? event[0]?.theme : null); + if (theme && window.Alpine && Alpine.store('theme')) { + Alpine.store('theme').apply(theme); + } + }); + }); + // copy-to-clipboard Livewire event document.addEventListener('livewire:init', function () { Livewire.on('copy-to-clipboard', function (event) { diff --git a/resources/views/livewire/pages/bio/index.blade.php b/resources/views/livewire/pages/bio/index.blade.php index af32b57..ebded00 100644 --- a/resources/views/livewire/pages/bio/index.blade.php +++ b/resources/views/livewire/pages/bio/index.blade.php @@ -36,6 +36,7 @@
+ diff --git a/resources/views/livewire/pages/dashboard/overview.blade.php b/resources/views/livewire/pages/dashboard/overview.blade.php index 5bec026..d27119c 100644 --- a/resources/views/livewire/pages/dashboard/overview.blade.php +++ b/resources/views/livewire/pages/dashboard/overview.blade.php @@ -6,18 +6,18 @@

{{ now()->format('l, F j') }}

- Analytics - + QR Code - + New Link @@ -129,7 +129,7 @@
diff --git a/resources/views/livewire/pages/links/index.blade.php b/resources/views/livewire/pages/links/index.blade.php index 60e9e21..864d84e 100644 --- a/resources/views/livewire/pages/links/index.blade.php +++ b/resources/views/livewire/pages/links/index.blade.php @@ -100,6 +100,7 @@ {{-- Actions --}}
+
+ @php $qrUrl = $qr->payload['data'] ?? ($qr->payload['url'] ?? null); @endphp + @if($qrUrl) + + @endif diff --git a/resources/views/livewire/pages/settings/api-tokens.blade.php b/resources/views/livewire/pages/settings/api-tokens.blade.php index 999104c..bacaf74 100644 --- a/resources/views/livewire/pages/settings/api-tokens.blade.php +++ b/resources/views/livewire/pages/settings/api-tokens.blade.php @@ -18,6 +18,9 @@

Token created — copy it now, it won't be shown again

{{ $newToken }} +
+ +