Add device, platform, and country analytics to link show page.
Deploy Ladill Link / deploy (push) Successful in 32s

Record parsed user-agent and geo data on clicks, then surface breakdowns and richer recent-click context on per-link and account analytics views.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-27 19:32:40 +00:00
co-authored by Cursor
parent 9d89aef17e
commit 0e5e0f6ca5
12 changed files with 453 additions and 32 deletions
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Support\Link;
use Illuminate\Http\Request;
final class LinkGeoResolver
{
public static function countryCode(Request $request): ?string
{
foreach (['CF-IPCountry', 'X-Country-Code', 'CloudFront-Viewer-Country'] as $header) {
$value = $request->header($header);
if (! is_string($value) || strlen($value) !== 2) {
continue;
}
$code = strtoupper($value);
if (! in_array($code, ['XX', 'T1'], true)) {
return $code;
}
}
return null;
}
public static function countryLabel(?string $code): string
{
if ($code === null || $code === '') {
return 'Unknown';
}
if (function_exists('locale_get_display_region')) {
$name = locale_get_display_region('_'.$code, 'en');
return is_string($name) && $name !== '' ? $name : $code;
}
return $code;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace App\Support\Link;
final class LinkUserAgentParser
{
/**
* @return array{device_type: string, browser: string, os: string}
*/
public static function parse(string $userAgent): array
{
$uaLower = strtolower($userAgent);
$device = str_contains($uaLower, 'mobile') || str_contains($uaLower, 'android') || str_contains($uaLower, 'iphone')
? 'mobile'
: (str_contains($uaLower, 'tablet') || str_contains($uaLower, 'ipad') ? 'tablet' : 'desktop');
$browser = match (true) {
str_contains($uaLower, 'edg/') => 'Edge',
str_contains($uaLower, 'chrome/') && ! str_contains($uaLower, 'edg/') => 'Chrome',
str_contains($uaLower, 'safari/') && ! str_contains($uaLower, 'chrome/') => 'Safari',
str_contains($uaLower, 'firefox/') => 'Firefox',
str_contains($uaLower, 'instagram') => 'Instagram',
str_contains($uaLower, 'whatsapp') => 'WhatsApp',
default => 'Other',
};
$os = match (true) {
str_contains($uaLower, 'iphone') || str_contains($uaLower, 'ipad') => 'iOS',
str_contains($uaLower, 'android') => 'Android',
str_contains($uaLower, 'windows') => 'Windows',
str_contains($uaLower, 'mac os') || str_contains($uaLower, 'macintosh') => 'macOS',
str_contains($uaLower, 'linux') => 'Linux',
default => 'Other',
};
return [
'device_type' => $device,
'browser' => $browser,
'os' => $os,
];
}
}