Laravel Livewire + Apexcharts
<?php | |
namespace App\Http\Livewire\Stats; | |
use Illuminate\Support\Facades\DB; | |
use Livewire\Component; | |
class DistanceByYear extends Component | |
{ | |
public $chartId; | |
public $athlete; | |
public $years; | |
public $distances; | |
public $total; | |
// Filters | |
public $bike; // aka bike_id; looks cleaner in the query string | |
public function render() | |
{ | |
$distancesByYear = $this->getDistancesByYear(); | |
$this->years = $distancesByYear->pluck('year'); | |
$this->distances = $distancesByYear | |
->pluck('total_distance') | |
->map(function ($distance) { | |
return session()->get('unit') === 'feet' | |
? metersToMiles($distance) | |
: metersToKm($distance); | |
}); | |
$this->total = $this->distances->sum(); | |
$this->emit("refreshChartData-{$this->chartId}", [ | |
'seriesData' => $this->distances, | |
'categories' => $this->years, | |
'seriesName' => 'Total distance this year', | |
]); | |
return view('livewire.stats.distance-by-year'); | |
} | |
public function resetFilter($filter) | |
{ | |
$this->{$filter} = null; | |
} | |
private function getDistancesByYear() | |
{ | |
$query = DB::table('activities') | |
->select( | |
DB::raw('YEAR(start_date_local) AS year'), | |
DB::raw('SUM(distance) AS total_distance'), | |
) | |
->where(['athlete_id' => $this->athlete->id]); | |
if ($this->bike > 0) { | |
$query->where('bike_id', $this->bike); | |
} | |
return $query | |
->groupByRaw('YEAR(start_date_local)') | |
->get(); | |
} | |
} |
<?php | |
namespace App\View\Components; | |
use Illuminate\View\Component; | |
class ApexCharts extends Component | |
{ | |
public string $chartId; | |
public $seriesData; | |
public $categories; | |
public $seriesName; | |
public function __construct($chartId, $seriesData, $categories, $seriesName = '') | |
{ | |
$this->chartId = $chartId; | |
$this->seriesData = $seriesData; | |
$this->categories = $categories; | |
$this->seriesName = $seriesName ?? 'Series'; | |
} | |
public function render() | |
{ | |
return view('components.apex-charts'); | |
} | |
} |
<div id="{!! $chartId !!}"></div> | |
@push('scripts') | |
<script> | |
(function () { | |
const options = { | |
chart: { | |
id: `{!! $chartId !!}`, | |
type: 'bar' | |
}, | |
plotOptions: { | |
bar: { | |
barHeight: '100%', | |
distributed: true, | |
dataLabels: { | |
// position: 'bottom' | |
}, | |
} | |
}, | |
xaxis: { | |
type: 'category', | |
categories: {!! $categories !!} | |
}, | |
series: [{ | |
name: `{!! $seriesName !!}`, | |
data: {!! $seriesData !!} | |
}], | |
// colors: ['#5c6ac4', '#007ace'], | |
} | |
const chart = new ApexCharts(document.getElementById(`{!! $chartId !!}`), options); | |
chart.render(); | |
document.addEventListener('livewire:load', () => { | |
@this.on(`refreshChartData-{!! $chartId !!}`, (chartData) => { | |
chart.updateOptions({ | |
xaxis: { | |
categories: chartData.categories | |
} | |
}); | |
chart.updateSeries([{ | |
data: chartData.seriesData, | |
name: chartData.seriesName, | |
}]); | |
}); | |
}); | |
}()); | |
</script> | |
@endpush |
<div class="w-full"> | |
<div class="bg-white shadow-2xl rounded"> | |
<x-section-header> | |
Distance By Year | |
<x-slot name="content"> | |
<span class="text-xl font-normal"> | |
{{$total}} @include('partials.unit._distance') | |
</span> | |
</x-slot> | |
</x-section-header> | |
<section class="p-2"> | |
<!-- Filters --> | |
<!-- Bike --> | |
<div class="flex flex-col"> | |
<label for="bike" class="flex flex-col w-1/4 font-medium text-xs text-black tracking-tight uppercase"> | |
Bike | |
</label> | |
<div class="flex"> | |
<select name="bike" id="bike" class="border px-2 py-1 rounded shadow-inner text-sm" wire:model="bike"> | |
<option>All</option> | |
@foreach($athlete->bikes as $bikeFilter) | |
<option value="{{ $bikeFilter->id }}">{{ $bikeFilter->name }}</option> | |
@endforeach | |
</select> | |
@if(!empty($bike) && $bike !== 'All') | |
<button wire:click="resetFilter('bike')" class="px-2 text-xl" title="Remove Bike filter"> | |
<x-icon icon="teenyicons-x" width=15 height=15 viewBox="15 15" strokeWidth=1 /> | |
</button> | |
@endif | |
</div> | |
</div> | |
<x-apex-charts :chart-id="$chartId" :series-data="$distances" :categories="$years" series-name="Total distance this year"/> | |
</section> | |
</div> | |
</div> |
@extends('layouts.connected') | |
@section('connected-content') | |
<div class="grid grid-cols-none xl:grid-cols-2 gap-4 w-full"> | |
<livewire:stats.distance-by-year :athlete="$athlete" chart-id="distance-by-year" key="distance-by-year"/> | |
<livewire:stats.distance-by-month :athlete="$athlete" chart-id="distance-by-month" key="distance-by-month"/> | |
</div> | |
@endsection | |
@prepend('scripts') | |
{{-- Push ApexCharts to the top of the scripts stack --}} | |
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script> | |
@endprepend |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment