Skip to content

Instantly share code, notes, and snippets.

@breadthe
Last active January 22, 2024 06:26
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save breadthe/1ed8eec0b464d511877b06a04898bbef to your computer and use it in GitHub Desktop.
Save breadthe/1ed8eec0b464d511877b06a04898bbef to your computer and use it in GitHub Desktop.
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