]> _ Git - fluidbook-toolbox.git/commitdiff
Select period segmentation from date picker + other improvements. WIP #5316 @8
authorStephen Cameron <stephen@cubedesigners.com>
Tue, 27 Sep 2022 17:26:36 +0000 (19:26 +0200)
committerStephen Cameron <stephen@cubedesigners.com>
Tue, 27 Sep 2022 17:26:36 +0000 (19:26 +0200)
app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php
public/packages/daterangepicker/daterangepicker.css
resources/views/fluidbook_stats/loader.blade.php
resources/views/fluidbook_stats/summary.blade.php

index 930a005e57994e74c8cad2da9b8a0634c8f64b96..f900f03bd1c7186b5dfd9fe04096a8b862ee40e8 100644 (file)
@@ -178,10 +178,22 @@ trait StatsOperation
 
         // Translatable list of labels for the available periods
         $period_map = [
-            'day'   => __('Day'),
-            'week'  => __('Week'),
-            'month' => __('Month'),
-            'year'  => __('Year'),
+            'day'   => [
+                'singular' => __('Day'),
+                'periodic' => __('Daily'),
+            ],
+            'week'  => [
+                'singular' => __('Week'),
+                'periodic' => __('Weekly'),
+            ],
+            'month' => [
+                'singular' => __('Month'),
+                'periodic' => __('Monthly'),
+            ],
+            'year'  => [
+                'singular' => __('Year'),
+                'periodic' => __('Annual'),
+            ],
         ];
 
         $dates = $date ? $this->parseDate($date) : false;
@@ -190,6 +202,7 @@ trait StatsOperation
         $period_override = in_array($period_override, array_keys($period_map)) ? $period_override : null;
         $period = $period_override ?? $this->determinePeriod($dates, $fluidbook);
 
+        $chart_heading = sprintf(__('%s Details'), $period_map[$period]['periodic']);
         $report_timespan = '';
 
         // Which mode are we in?
@@ -200,7 +213,6 @@ trait StatsOperation
             $date_range = "{$start_date},{$end_date}";
             $formatted_date_range = Carbon::parse($start_date)->isoFormat('MMMM Do, YYYY') . ' &mdash; ' .
                                     Carbon::parse($end_date)->isoFormat('MMMM Do, YYYY');
-            $chart_heading = __('Daily Details');
 
             // Human-friendly representation of the time span
             // Since our start and end dates are only in the format YYYY-MM-DD, the time defaults to midnight
@@ -222,8 +234,7 @@ trait StatsOperation
             $start_date = "{$year}-{$month}-01";
             $date_range = "{$start_date},{$year}-{$month}-{$last_day_of_month}";
             $end_date = ($month == date('m') && $year == date('Y')) ? date('Y-m-d') : "{$year}-{$month}-{$last_day_of_month}";
-            $chart_heading = __('Daily Details') .
-                             '<span class="heading-subtitle">' . Carbon::parse($start_date)->isoFormat('MMMM YYYY') . '</span>';
+            $chart_heading .= '<span class="heading-subtitle">' . Carbon::parse($start_date)->isoFormat('MMMM YYYY') . '</span>';
             $formatted_date_range = Carbon::parse($start_date)->isoFormat('Do') . ' &mdash; ' .
                                     Carbon::parse($end_date)->isoFormat('Do MMMM, YYYY');
 
@@ -235,7 +246,7 @@ trait StatsOperation
             $end_date = $year == date('Y') ? date('Y-m-d') : "{$year}-12-31"; // If it's the current year, don't count future dates
             $formatted_date_range = Carbon::parse($start_date)->isoFormat('MMMM Do, YYYY') . ' &mdash; ' .
                                     Carbon::parse($end_date)->isoFormat('MMMM Do, YYYY');
-            $chart_heading = __('Annual Details') . "<span class='heading-subtitle'>$year</span>";
+            $chart_heading .= "<span class='heading-subtitle'>$year</span>";
 
         } else { // No valid dates specified, display the full data set
             $mode = 'overview';
@@ -550,7 +561,7 @@ trait StatsOperation
         $table_map = [
             // Main summary table
             'summary' => [
-                'formatted_date'   => $period_map[$period],
+                'formatted_date'   => $period_map[$period]['singular'],
                 'nb_uniq_visitors' => __('Unique Visitors'),
                 'nb_visits'        => __('Visits'),
                 'nb_hits'          => __('Pages Viewed'),
index 3ca56fc5fb000cb0c1715f3f9356d8ead84a83a4..6e4d8745b9042be0a663e72a95c91968b5522549 100755 (executable)
@@ -13,7 +13,7 @@
   -ms-flex-pack: start;
       justify-content: flex-start;
   border-radius: 4px;
-  padding: 4px;
+  padding: 0.5em 0.25em;
   font-size: 13px;
   font-family: sans-serif;
   line-height: 1.5em;
 
 .daterangepicker .ranges li {
   border-radius: 4px;
-  margin-bottom: 8px;
   text-align: left;
 }
+.daterangepicker .ranges li + li {
+    margin-top: 0.5em;
+}
 
 .daterangepicker .custom-range-inputs {
-  display: -ms-flexbox;
   display: flex;
-  margin: -3px;
-  margin-bottom: 5px;
+  align-items: center;
+  gap: 0.5em;
+  margin-top: 0.5em;
 }
 
 .daterangepicker .custom-range-inputs input {
-  min-width: 50px;
-  width: 50px;
-  -ms-flex: 1;
-      flex: 1;
-  margin: 3px;
+  min-width: 11ch;
+  flex: 1;
   border-radius: 4px;
   border: 1px solid #ccc;
   height: auto;
 }
 
 .daterangepicker .custom-range-buttons {
-  display: -ms-flexbox;
   display: flex;
-  margin: -3px;
+  gap: 0.5em;
+  margin-top: 1em;
 }
 
 .daterangepicker .custom-range-buttons button {
-  margin: 0;
   padding: 4px 9px;
-  margin: 3px;
   border-radius: 4px;
   background: #f5f5f5;
   color: #08c;
index f90ee6386bbf690175b4b763c0f54e475420aac6..d60607e9b2909929ca65e05f69e6112012fd69e0 100644 (file)
@@ -3,6 +3,11 @@
 @section('after_styles')
     <link rel="stylesheet" href="{{ asset('packages/daterangepicker/daterangepicker.css') }}">
     <style>
+        .container-fluid.animated.fadeIn {
+            {{-- Disable entrance animation on this page because we already have the stats loader --}}
+            animation: none;
+        }
+
         @keyframes spinner {
             to {
                 transform: rotate(360deg);
             margin: 5em auto;
         }
 
+        .chart-header {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+        }
 
         .periods {
             display: inline-flex;
             margin-left: 2em;
         }
         .periods > * {
-            padding: 0.2em 0.8em;
+            padding: 0.35em 0.7em;
             border: 1px solid #ddd;
+            line-height: 1;
         }
         .periods > * + * {
             border-left: 0;
             width: 100%;
         }
         table.stats-details th, table.stats-details td {
-            padding: 0.5em 1em;
+            padding: 0.5em 0.75em;
         }
         table.stats-details thead tr {
             position: sticky;
             background-color: rgba(156, 195, 34, 0.15);
         }
 
+        [data-name="formatted_date"] {
+            white-space: nowrap;
+        }
+
         .table-columns {
             display: flex;
             flex-wrap: wrap;
         [data-daterangepicker]:hover {
             color: #467fcf;
         }
+
+        .daterangepicker.expanded {
+            padding-bottom: 0.75em;
+        }
+
         .daterangepicker .periods li:hover, .daterangepicker .periods li.active, .daterangepicker .ranges li:hover, .daterangepicker .ranges li.active {
             background: #263340;
         }
         .daterangepicker .periods li, .daterangepicker .ranges li {
             color: #467fcf;
         }
+
+        .daterangepicker .ranges {
+            margin-bottom: 1em;
+        }
+
+        .daterangepicker .custom-range-inputs input {
+            padding: 0.25em 0.5em;
+            width: 11ch;
+        }
+        .daterangepicker .custom-range-inputs .arrow-right {
+            opacity: 0.5;
+        }
+        .daterangepicker .custom-range-buttons {
+            flex-direction: row-reverse;
+        }
+        .daterangepicker.expanded .custom-range-buttons {
+            position: absolute;
+            right: 1em;
+            bottom: 0.75em;
+        }
         .daterangepicker .custom-range-buttons button {
             color: #467fcf;
         }
             background: #263340;
         }
 
+        .daterangepicker select[name="segmentation"] {
+            border: 1px solid #ccc;
+            border-radius: 0.25em;
+            padding: 0.25em;
+            text-align: left;
+            color: #333;
+            margin: 0;
+        }
+
         /*=== Table Column Sorter ===*/
         .sortable th:not(.sorttable_nosort) {
             cursor: pointer;
             showStatsError();
         });
 
-
         function hideStatsLoader() {
             document.getElementById('stats_loader').style.display = 'none';
         }
 
+        function showStatsLoader() {
+            document.getElementById('stats_wrapper').style.display = 'none';
+            document.getElementById('stats_loader').style.display = 'flex';
+        }
+
         function showStatsError() {
             hideStatsLoader();
             document.getElementById('stats_error').style.display = 'block';
index 0deba1d52ff105e6adcd5049d83f9fef8280c674..8b1c7ddc9cc7ad8fc680016996ac8bc139a3a4fc 100644 (file)
@@ -6,7 +6,7 @@
     <span class="heading-subtitle">{{ $fluidbook->name }}</span>
 </h2>
 
-<span data-daterangepicker class="mb-2" style="cursor: pointer" title="{{ __('Statistics Period - click to choose a new date range') }}">
+<div data-daterangepicker class="mb-4" style="cursor: pointer" title="{{ __('Statistics Period - click to choose a new date range') }}">
     <i class="las la-calendar-week align-middle mr-1" style="font-size: 32px;"></i>
     <span class="date-range-text">
         {!! $formatted_date_range !!}
             <span style="opacity: 0.6; display: inline-block; margin-left: 0.5em;">({{ $report_timespan }})</span>
         @endif
     </span>
-</span>
-
-<div class="periods mb-4">
-    @foreach($period_map as $period_key => $period_title)
-        @if($period_key === $period)
-            <span>{{ $period_title }}</span>
-        @else
-            <a href="{{ route('stats', compact('fluidbook_id', 'hash') + ['date' => $date ?? '-', 'period_override' => $period_key]) }}">
-                {{ $period_title }}
-            </a>
-        @endif
-    @endforeach
 </div>
 
 
 
 @if($period_details->isNotEmpty())
 
-    <h2 class="mt-5">{!! $chart_heading !!}</h2>
+    <div class="chart-header mt-5">
+        <h2>{!! $chart_heading !!}</h2>
+        <div class="periods">
+            @foreach($period_map as $period_key => $period_title)
+                @if($period_key === $period)
+                    <span>{{ $period_title['singular'] }}</span>
+                @else
+                    <a href="{{ route('stats', compact('fluidbook_id', 'hash') + ['date' => $date ?? '-', 'period_override' => $period_key]) }}" onclick="showStatsLoader()">
+                        {{ $period_title['singular'] }}
+                    </a>
+                @endif
+            @endforeach
+        </div>
+    </div>
 
     {{-- Chart --}}
     <canvas id="stats_chart"></canvas>
 
     $('[data-daterangepicker]').daterangepicker({
         callback: function(startDate, endDate, period) {
+            showStatsLoader();
             let range = startDate.format('YYYY-MM-DD') + ',' + endDate.format('YYYY-MM-DD');
-            location.href = `{{ $base_URL }}/${range}`;
-
-            // TODO: add loading spinner after changing the date so that we know something is happening while the next page loads...
-
+            let segmentation = document.querySelector('.daterangepicker [name="segmentation"]')?.value || false;
+            let periodOverride = segmentation ? `/${segmentation}` : '';
+            location.href = `{{ $base_URL }}/${range}${periodOverride}`;
         },
         forceUpdate: false,
         minDate: '{{ $fluidbook->created_at->isoFormat('YYYY-MM-DD') }}', // Creation date of the Fluidbook
         --}}
     });
 
+    {{-- Inject the segmentation (period) dropdown into the date picker --}}
+    let picker = document.querySelector('.daterangepicker');
+
+    let segmentOptions = '<option value="">{{ __('Segmentation:') }} {{ __('Automatic') }}</option>';
+    @foreach ($period_map as $period_key => $period_title)
+        segmentOptions += '<option value="{{ $period_key }}">{{ __('Segmentation:') }} {{ $period_title['periodic'] }}</option>'
+    @endforeach
+
+    let segmentation = document.createElement('select');
+    segmentation.name = 'segmentation';
+    segmentation.innerHTML = segmentOptions;
+    picker.querySelector('form').prepend(segmentation);
+
+    // Add arrow between range inputs
+    let rangeInputs = picker.querySelector('.custom-range-inputs');
+    let arrow = document.createElement('span');
+    arrow.className = 'arrow-right';
+    rangeInputs.insertBefore(arrow, rangeInputs.children[1]); // Insert in 2nd position
+
+
 </script>
 
 {{--============================================================================================================--}}