feat(analytics): live dashboard with Reverb WebSocket, click event broadcasting
Overwrites the Analytics Index stub with a full Livewire component that
loads today's click count from the clicks table (workspace_id is a direct
column) and listens for live updates via Echo/Reverb using the
#[On('echo:workspace.{workspaceId}.analytics,click.recorded')] attribute.
Creates the corresponding blade view with a today-counter card and a
live recent-clicks feed (last 10 entries).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
parent
61718fa867
commit
79da7502cd
|
|
@ -2,13 +2,38 @@
|
|||
|
||||
namespace App\Livewire\Pages\Analytics;
|
||||
|
||||
use App\Domains\Analytics\Models\Click;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\On;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public array $recentClicks = [];
|
||||
public int $totalToday = 0;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$workspace = app('current_workspace');
|
||||
|
||||
$this->totalToday = Click::where('workspace_id', $workspace->id)
|
||||
->whereDate('clicked_at', today())
|
||||
->count();
|
||||
}
|
||||
|
||||
#[On('echo:workspace.{workspaceId}.analytics,click.recorded')]
|
||||
public function handleNewClick(array $data): void
|
||||
{
|
||||
array_unshift($this->recentClicks, $data);
|
||||
$this->recentClicks = array_slice($this->recentClicks, 0, 10);
|
||||
$this->totalToday++;
|
||||
}
|
||||
|
||||
public function render(): \Illuminate\View\View
|
||||
{
|
||||
return view('livewire.pages.coming-soon')
|
||||
->layout('layouts.nimuli-app', ['title' => 'Analytics']);
|
||||
$workspace = app('current_workspace');
|
||||
|
||||
return view('livewire.pages.analytics.index', [
|
||||
'workspace' => $workspace,
|
||||
])->layout('layouts.nimuli-app', ['title' => 'Analytics']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<div>
|
||||
<h1 class="text-2xl font-bold mb-6">Analytics</h1>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||
<div class="bg-gray-900 rounded-xl border border-gray-800 p-4">
|
||||
<div class="text-xs text-gray-500 uppercase tracking-wider mb-1">Today</div>
|
||||
<div class="text-3xl font-bold font-mono">{{ $totalToday }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(count($recentClicks) > 0)
|
||||
<div class="bg-gray-900 rounded-xl border border-gray-800 overflow-hidden">
|
||||
<div class="px-4 py-3 border-b border-gray-800 text-sm font-medium">Recent clicks</div>
|
||||
<div class="divide-y divide-gray-800">
|
||||
@foreach($recentClicks as $click)
|
||||
<div class="px-4 py-3 text-sm flex items-center gap-4">
|
||||
<span class="text-gray-500 font-mono text-xs">{{ $click['clicked_at'] ?? '' }}</span>
|
||||
<span class="text-gray-400">{{ $click['country'] ?? '—' }}</span>
|
||||
<span class="text-gray-400">{{ $click['device'] ?? '—' }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
Loading…
Reference in New Issue