Skip to content

Commit df5a75c

Browse files
authored
Merge pull request #54 from me-shaon/feature/view-all-modal
Add view all modal functionality to analytics components
2 parents 8502862 + 24f8f2d commit df5a75c

File tree

10 files changed

+241
-19
lines changed

10 files changed

+241
-19
lines changed

resources/views/components/analytics/broswers.blade.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@props([
22
'browsers' => [],
3+
'allBrowsers' => null,
34
])
45

56
@php
@@ -14,10 +15,30 @@ function getBrowserImage($browser): string {
1415
};
1516
}
1617
}
18+
19+
$displayBrowsers = array_slice($browsers, 0, 5);
20+
$allBrowsersData = $allBrowsers ?? $browsers;
21+
$hasMore = count($allBrowsersData) > 5;
22+
23+
$modalItems = collect($allBrowsersData)->map(function($browser) {
24+
return [
25+
'label' => $browser['browser'],
26+
'count' => $browser['count'],
27+
'percentage' => $browser['percentage'],
28+
'imgSrc' => getBrowserImage($browser['browser'])
29+
];
30+
})->toArray();
1731
@endphp
1832

19-
<x-request-analytics::stats.list primaryLabel="Browser" secondaryLabel="">
20-
@forelse($browsers as $browser)
33+
<x-request-analytics::stats.list
34+
primaryLabel="Browser"
35+
secondaryLabel=""
36+
:showViewAll="$hasMore"
37+
modalTitle="All Browsers"
38+
:allItems="$modalItems"
39+
modalId="browsers-modal"
40+
>
41+
@forelse($displayBrowsers as $browser)
2142
<x-request-analytics::stats.item
2243
label="{{ $browser['browser'] }}"
2344
count="{{ $browser['count'] }}"

resources/views/components/analytics/countries.blade.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
@props([
22
'countries' => [],
3+
'allCountries' => null,
34
])
45

6+
@php
7+
$displayCountries = array_slice($countries, 0, 5);
8+
$allCountriesData = $allCountries ?? $countries;
9+
$hasMore = count($allCountriesData) > 5;
10+
11+
$modalItems = collect($allCountriesData)->map(function($country) {
12+
return [
13+
'label' => $country['name'],
14+
'count' => $country['count'],
15+
'percentage' => $country['percentage'],
16+
'imgSrc' => "https://www.worldatlas.com/r/w236/img/flag/{$country['code']}-flag.jpg"
17+
];
18+
})->toArray();
19+
@endphp
520

6-
<x-request-analytics::stats.list primaryLabel="Countries" secondaryLabel="">
7-
@forelse($countries as $country)
21+
<x-request-analytics::stats.list
22+
primaryLabel="Countries"
23+
secondaryLabel=""
24+
:showViewAll="$hasMore"
25+
modalTitle="All Countries"
26+
:allItems="$modalItems"
27+
modalId="countries-modal"
28+
>
29+
@forelse($displayCountries as $country)
830
<x-request-analytics::stats.item
931
label="{{ $country['name'] }}"
1032
count="{{ $country['count'] }}"

resources/views/components/analytics/devices.blade.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@props([
22
'devices' => [],
3+
'allDevices' => null,
34
])
45

56
@php
@@ -14,9 +15,30 @@ function getDeviceImage($device): string {
1415
};
1516
}
1617
}
18+
19+
$displayDevices = array_slice($devices, 0, 5);
20+
$allDevicesData = $allDevices ?? $devices;
21+
$hasMore = count($allDevicesData) > 5;
22+
23+
$modalItems = collect($allDevicesData)->map(function($device) {
24+
return [
25+
'label' => $device['name'],
26+
'count' => $device['count'],
27+
'percentage' => $device['percentage'],
28+
'imgSrc' => getDeviceImage($device['name'])
29+
];
30+
})->toArray();
1731
@endphp
18-
<x-request-analytics::stats.list primaryLabel="Devices" secondaryLabel="">
19-
@forelse($devices as $device)
32+
33+
<x-request-analytics::stats.list
34+
primaryLabel="Devices"
35+
secondaryLabel=""
36+
:showViewAll="$hasMore"
37+
modalTitle="All Devices"
38+
:allItems="$modalItems"
39+
modalId="devices-modal"
40+
>
41+
@forelse($displayDevices as $device)
2042
<x-request-analytics::stats.item
2143
label="{{ $device['name'] }}"
2244
count="{{ $device['count'] }}"

resources/views/components/analytics/operating-systems.blade.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@props([
22
'operatingSystems' => [],
3+
'allOperatingSystems' => null,
34
])
45

56
@php
@@ -21,9 +22,30 @@ function getOperatingSystemImage($os): string {
2122
};
2223
}
2324
}
25+
26+
$displayOperatingSystems = array_slice($operatingSystems, 0, 5);
27+
$allOperatingSystemsData = $allOperatingSystems ?? $operatingSystems;
28+
$hasMore = count($allOperatingSystemsData) > 5;
29+
30+
$modalItems = collect($allOperatingSystemsData)->map(function($os) {
31+
return [
32+
'label' => $os['name'],
33+
'count' => $os['count'],
34+
'percentage' => $os['percentage'],
35+
'imgSrc' => getOperatingSystemImage($os['name'])
36+
];
37+
})->toArray();
2438
@endphp
25-
<x-request-analytics::stats.list primaryLabel="Operating Systems" secondaryLabel="">
26-
@forelse($operatingSystems as $os)
39+
40+
<x-request-analytics::stats.list
41+
primaryLabel="Operating Systems"
42+
secondaryLabel=""
43+
:showViewAll="$hasMore"
44+
modalTitle="All Operating Systems"
45+
:allItems="$modalItems"
46+
modalId="operating-systems-modal"
47+
>
48+
@forelse($displayOperatingSystems as $os)
2749
<x-request-analytics::stats.item
2850
label="{{ $os['name'] }}"
2951
count="{{ $os['count'] }}"

resources/views/components/analytics/pages.blade.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
@props([
22
'pages' => [],
3+
'allPages' => null,
34
])
45

5-
<x-request-analytics::stats.list primaryLabel="Pages" secondaryLabel="Views">
6-
@forelse($pages as $page)
6+
@php
7+
$displayPages = array_slice($pages, 0, 5);
8+
$allPagesData = $allPages ?? $pages;
9+
$hasMore = count($allPagesData) > 5;
10+
11+
$modalItems = collect($allPagesData)->map(function($page) {
12+
return [
13+
'label' => $page['path'],
14+
'count' => $page['views'],
15+
'percentage' => $page['percentage'],
16+
'imgSrc' => null
17+
];
18+
})->toArray();
19+
@endphp
20+
21+
<x-request-analytics::stats.list
22+
primaryLabel="Pages"
23+
secondaryLabel="Views"
24+
:showViewAll="$hasMore"
25+
modalTitle="All Pages"
26+
:allItems="$modalItems"
27+
modalId="pages-modal"
28+
>
29+
@forelse($displayPages as $page)
730
<x-request-analytics::stats.item
831
label="{{ $page['path'] }}"
932
count="{{ $page['views'] }}"

resources/views/components/analytics/referrers.blade.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
@props([
22
'referrers' => [],
3+
'allReferrers' => null,
34
])
45

5-
<x-request-analytics::stats.list primaryLabel="Referrers" secondaryLabel="Views">
6-
@forelse($referrers as $referrer)
6+
@php
7+
$displayReferrers = array_slice($referrers, 0, 5);
8+
$allReferrersData = $allReferrers ?? $referrers;
9+
$hasMore = count($allReferrersData) > 5;
10+
11+
$modalItems = collect($allReferrersData)->map(function($referrer) {
12+
return [
13+
'label' => $referrer['domain'],
14+
'count' => $referrer['visits'],
15+
'percentage' => $referrer['percentage'],
16+
'imgSrc' => null
17+
];
18+
})->toArray();
19+
@endphp
20+
21+
<x-request-analytics::stats.list
22+
primaryLabel="Referrers"
23+
secondaryLabel="Views"
24+
:showViewAll="$hasMore"
25+
modalTitle="All Referrers"
26+
:allItems="$modalItems"
27+
modalId="referrers-modal"
28+
>
29+
@forelse($displayReferrers as $referrer)
730
<x-request-analytics::stats.item
831
label="{{ $referrer['domain'] }}"
932
count="{{ $referrer['visits'] }}"

resources/views/components/stats/list.blade.php

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
@props([
22
'primaryLabel',
33
'secondaryLabel',
4-
'footer' => null
4+
'footer' => null,
5+
'showViewAll' => false,
6+
'modalTitle' => null,
7+
'allItems' => [],
8+
'modalId' => null
59
])
610

11+
@php
12+
$modalId = $modalId ?? 'modal-' . Str::random(8);
13+
$modalTitle = $modalTitle ?? $primaryLabel;
14+
@endphp
15+
716
<div class="p-6 min-h-[400px] flex flex-col">
817
<div class="flex items-center justify-between mb-4">
918
<h3 class="text-lg font-semibold text-gray-900">{{ $primaryLabel }}</h3>
10-
<span class="text-sm font-medium text-gray-500">{{ $secondaryLabel }}</span>
19+
<div class="flex items-center gap-3">
20+
@if($showViewAll && count($allItems) > 0)
21+
<button
22+
type="button"
23+
onclick="openModal('{{ $modalId }}')"
24+
class="text-xs font-medium text-blue-600 hover:text-blue-800 transition-colors duration-150"
25+
>
26+
View All
27+
</button>
28+
@endif
29+
<span class="text-sm font-medium text-gray-500">{{ $secondaryLabel }}</span>
30+
</div>
1131
</div>
1232
<div class="flex-1 flex flex-col space-y-3">
1333
{{ $slot }}
@@ -18,3 +38,74 @@
1838
</div>
1939
@endif
2040
</div>
41+
42+
@if($showViewAll && count($allItems) > 0)
43+
<!-- Modal -->
44+
<div id="{{ $modalId }}" class="modal-overlay fixed inset-0 bg-black bg-opacity-50 z-50 hidden items-center justify-center p-4">
45+
<div class="modal-content bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[80vh] overflow-hidden">
46+
<div class="modal-header flex items-center justify-between p-6 border-b border-gray-200">
47+
<h2 class="text-xl font-semibold text-gray-900">{{ $modalTitle }}</h2>
48+
<button
49+
type="button"
50+
onclick="closeModal('{{ $modalId }}')"
51+
class="text-gray-400 hover:text-gray-600 transition-colors duration-150"
52+
>
53+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
54+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
55+
</svg>
56+
</button>
57+
</div>
58+
<div class="modal-body p-6 overflow-y-auto max-h-[60vh]">
59+
<div class="space-y-3">
60+
@foreach($allItems as $item)
61+
<x-request-analytics::stats.item
62+
:label="$item['label']"
63+
:count="$item['count']"
64+
:percentage="$item['percentage']"
65+
:imgSrc="$item['imgSrc'] ?? null"
66+
/>
67+
@endforeach
68+
</div>
69+
</div>
70+
</div>
71+
</div>
72+
@endif
73+
74+
@once
75+
<script>
76+
function openModal(modalId) {
77+
const modal = document.getElementById(modalId);
78+
if (modal) {
79+
modal.classList.remove('hidden');
80+
modal.classList.add('flex');
81+
document.body.style.overflow = 'hidden';
82+
}
83+
}
84+
85+
function closeModal(modalId) {
86+
const modal = document.getElementById(modalId);
87+
if (modal) {
88+
modal.classList.add('hidden');
89+
modal.classList.remove('flex');
90+
document.body.style.overflow = 'auto';
91+
}
92+
}
93+
94+
// Close modal when clicking outside
95+
document.addEventListener('click', function(e) {
96+
if (e.target.classList.contains('modal-overlay')) {
97+
closeModal(e.target.id);
98+
}
99+
});
100+
101+
// Close modal with Escape key
102+
document.addEventListener('keydown', function(e) {
103+
if (e.key === 'Escape') {
104+
const visibleModal = document.querySelector('.modal-overlay:not(.hidden)');
105+
if (visibleModal) {
106+
closeModal(visibleModal.id);
107+
}
108+
}
109+
});
110+
</script>
111+
@endonce

src/RequestAnalyticsServiceProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace MeShaon\RequestAnalytics;
44

5-
use MeShaon\RequestAnalytics\Commands\SetupCommand;
65
use Illuminate\Contracts\Http\Kernel;
76
use MeShaon\RequestAnalytics\Commands\RequestAnalyticsCommand;
7+
use MeShaon\RequestAnalytics\Commands\SetupCommand;
88
use MeShaon\RequestAnalytics\Http\Middleware\AnalyticsDashboardMiddleware;
99
use MeShaon\RequestAnalytics\Http\Middleware\APIRequestCapture;
1010
use MeShaon\RequestAnalytics\Http\Middleware\WebRequestCapture;

tests/Feature/Commands/SetupCommandTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
<?php
2+
23
declare(strict_types=1);
34

45
namespace MeShaon\RequestAnalytics\Tests\Feature\Commands;
56

67
use Illuminate\Support\Facades\File;
78
use Illuminate\Support\Facades\Schema;
8-
use Orchestra\Testbench\TestCase;
99
use MeShaon\RequestAnalytics\RequestAnalyticsServiceProvider;
10+
use Orchestra\Testbench\TestCase;
1011
use PHPUnit\Framework\Attributes\Test;
1112

1213
class SetupCommandTest extends TestCase
@@ -23,7 +24,6 @@ public function it_publishes_migrations_and_runs_only_package_migrations()
2324
$migrationPath = database_path('migrations');
2425
File::cleanDirectory($migrationPath);
2526

26-
2727
$this->artisan('laravel-request-analytics:setup')
2828
->expectsQuestion('How would you like to run migrations?',
2929
'Run only Request Analytics migrations (default)')

tests/Unit/Middleware/RequestCaptureMiddlewareTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,6 @@ public function web_middleware_terminates_and_queues_job_when_queue_enabled(): v
102102
Queue::assertPushed(ProcessData::class);
103103
}
104104

105-
106-
107105
#[Test]
108106
public function middleware_does_not_capture_bots_when_bot_capture_disabled(): void
109107
{

0 commit comments

Comments
 (0)