nimuli/app/Livewire/Pages/Analytics/Index.php

142 lines
3.8 KiB
PHP

<?php
namespace App\Livewire\Pages\Analytics;
use App\Domains\Analytics\Models\Click;
use App\Domains\Link\Models\Link;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\View\View;
use Livewire\Attributes\On;
use Livewire\Attributes\Url;
use Livewire\Component;
class Index extends Component
{
#[Url(as: 'range')]
public string $range = '7d';
#[Url(as: 'country')]
public string $country = '';
#[Url(as: 'device')]
public string $device = '';
#[Url(as: 'link')]
public string $linkId = '';
public int $totalToday = 0;
public int $workspaceId = 0;
/** @var array<int, array<string, mixed>> */
public array $recentClicks = [];
public function mount(): void
{
$workspace = require_workspace();
$this->workspaceId = $workspace->id;
$this->totalToday = Click::where('workspace_id', $workspace->id)
->whereDate('clicked_at', today())
->count();
}
public function setRange(string $range): void
{
$this->range = in_array($range, ['24h', '7d', '30d', '90d']) ? $range : '7d';
}
private function rangeStart(): Carbon
{
return match ($this->range) {
'24h' => now()->subHours(24),
'30d' => now()->subDays(30),
'90d' => now()->subDays(90),
default => now()->subDays(7),
};
}
/** @return Builder<Click> */
private function baseQuery(): Builder
{
$workspace = require_workspace();
$q = Click::where('workspace_id', $workspace->id)
->where('clicked_at', '>=', $this->rangeStart());
if ($this->country) {
$q->where('country', $this->country);
}
if ($this->device) {
$q->where('device', $this->device);
}
if ($this->linkId) {
$link = Link::where('workspace_id', $workspace->id)->where('ulid', $this->linkId)->first();
if ($link) {
$q->where('link_id', $link->id);
}
}
return $q;
}
/** @param array<string, mixed> $data */
#[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(): View
{
$workspace = require_workspace();
$totalFiltered = $this->baseQuery()->count();
$allTime = Click::where('workspace_id', $workspace->id)->count();
$byCountry = (clone $this->baseQuery())
->whereNotNull('country')
->selectRaw('country, COUNT(*) as total')
->groupBy('country')
->orderByDesc('total')
->limit(10)
->get();
$byDevice = (clone $this->baseQuery())
->selectRaw('device, COUNT(*) as total')
->groupBy('device')
->orderByDesc('total')
->get();
$countries = Click::where('workspace_id', $workspace->id)
->whereNotNull('country')
->distinct()
->pluck('country')
->sort()
->values();
$devices = Click::where('workspace_id', $workspace->id)
->whereNotNull('device')
->distinct()
->pluck('device')
->values();
$links = Link::where('workspace_id', $workspace->id)
->orderBy('slug')
->get(['ulid', 'slug', 'title']);
return view('livewire.pages.analytics.index', compact(
'totalFiltered',
'allTime',
'byCountry',
'byDevice',
'countries',
'devices',
'links',
))->layout('layouts.nimuli-app', ['title' => 'Analytics']);
}
}