Skip to content

Commit

Permalink
feat: top chart embeds (#72)
Browse files Browse the repository at this point in the history
* top counties widget

* top localities widget
  • Loading branch information
andreiio authored Nov 23, 2024
1 parent 07910bb commit 1c53901
Show file tree
Hide file tree
Showing 7 changed files with 357 additions and 0 deletions.
146 changes: 146 additions & 0 deletions app/Livewire/Charts/TopCountiesChart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Charts;

use App\Enums\DataLevel;
use App\Models\County;
use App\Models\Election;
use App\Repositories\TurnoutRepository;
use Filament\Support\Colors\Color;
use Filament\Support\RawJs;
use Filament\Widgets\ChartWidget;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;

class TopCountiesChart extends ChartWidget
{
public Election $election;

public ?Collection $topCounties = null;

protected static ?string $maxHeight = '1200px';

protected static ?string $pollingInterval = null;

public function getHeading(): Htmlable
{
return new HtmlString(view('components.chart-heading', [
'title' => 'Topul județelor după prezență',
'url' => route('front.elections.embed.top-counties', [
'election' => $this->election,
]),
])->render());
}

protected function getTopCounties(): Collection
{
if (blank($this->topCounties)) {
$counties = County::pluck('name', 'id');

$this->topCounties = TurnoutRepository::getForLevel(
election: $this->election,
level: DataLevel::NATIONAL,
toBase: true,
)
->map(function (object $turnout) use ($counties) {
$turnout->name = $counties->get($turnout->place);
$turnout->percent = percent($turnout->total, $turnout->initial_total);

return $turnout;
})
->sortByDesc('percent');
}

return $this->topCounties;
}

protected function getData(): array
{
$result = $this->getTopCounties();

$labels = [];
$data = [];
$backgroundColor = [];

foreach ($result as $county) {
$labels[] = $county->name;

$backgroundColor[] = 'rgb(' . Color::Blue[400] . ')';
$data[] = percent($county->total, $county->initial_total);
}

return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Prezența',
'borderWidth' => 0,
'backgroundColor' => $backgroundColor,
'data' => $data,
],
],
];
}

protected function getType(): string
{
return 'bar';
}

protected function getOptions(): RawJs
{
if ($this->getTopCounties()->isEmpty()) {
return RawJs::make(<<<'JS'
{
indexAxis: 'y',
scales: {
x: {
ticks: {
display: false
},
},
},
events: []
}
JS);
}

return RawJs::make(<<<'JS'
{
maintainAspectRatio: false,
aspectRatio: 0.15,
indexAxis: 'y',
scales: {
y: {
beginAtZero: true,
stacked: true,
},
x: {
beginAtZero: true,
},
},
plugins: {
tooltip: {
callbacks: {
label: (context) => {
let label = context.dataset.label;
console.log(context);
if (label) {
label += ': ';
}
if (context.parsed.x !== null) {
label += Math.abs(context.parsed.x) + ' %';
}
return label;
},
},
},
}
}
JS);
}
}
163 changes: 163 additions & 0 deletions app/Livewire/Charts/TopLocalitiesChart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Charts;

use App\Enums\DataLevel;
use App\Models\Election;
use App\Repositories\TurnoutRepository;
use Filament\Support\Colors\Color;
use Filament\Support\RawJs;
use Filament\Widgets\ChartWidget;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;

class TopLocalitiesChart extends ChartWidget
{
public Election $election;

public ?Collection $topLocalities = null;

protected static ?string $maxHeight = '1200px';

protected static ?string $pollingInterval = null;

public function getHeading(): Htmlable
{
return new HtmlString(view('components.chart-heading', [
'title' => 'Top 10 mari orașe din România',
'url' => route('front.elections.embed.top-localities', [
'election' => $this->election,
]),
])->render());
}

protected function getTopLocalities(): Collection
{
$localities = collect([
54975 => 'Cluj-Napoca',
95060 => 'Iași',
60419 => 'Constanța',
155243 => 'Timișoara',
40198 => 'Brașov',
69900 => 'Craiova',
75098 => 'Galați',
26564 => 'Oradea',
130534 => 'Ploiești',
]);

if (blank($this->topLocalities)) {
$this->topLocalities = $localities
->map(fn (string $name, int $locality) => TurnoutRepository::getForLevel(
election: $this->election,
level: DataLevel::NATIONAL,
locality: $locality,
toBase: true,
aggregate: true,
))
->prepend(TurnoutRepository::getForLevel(
election: $this->election,
level: DataLevel::NATIONAL,
county: 403, // București
toBase: true,
aggregate: true,
))
->map(function (object $turnout) use ($localities) {
$turnout->name = $localities->get($turnout->place, 'București');
$turnout->percent = percent($turnout->total, $turnout->initial_total);

return $turnout;
})
->sortByDesc('percent');
}

return $this->topLocalities;
}

protected function getData(): array
{
$result = $this->getTopLocalities();

$labels = [];
$data = [];
$backgroundColor = [];

foreach ($result as $county) {
$labels[] = $county->name;

$backgroundColor[] = 'rgb(' . Color::Blue[400] . ')';
$data[] = percent($county->total, $county->initial_total);
}

return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Prezența',
'borderWidth' => 0,
'backgroundColor' => $backgroundColor,
'data' => $data,
],
],
];
}

protected function getType(): string
{
return 'bar';
}

protected function getOptions(): RawJs
{
if ($this->getTopLocalities()->isEmpty()) {
return RawJs::make(<<<'JS'
{
indexAxis: 'y',
scales: {
x: {
ticks: {
display: false
},
},
},
events: []
}
JS);
}

return RawJs::make(<<<'JS'
{
indexAxis: 'y',
scales: {
y: {
beginAtZero: true,
stacked: true,
},
x: {
beginAtZero: true,
},
},
plugins: {
tooltip: {
callbacks: {
label: (context) => {
let label = context.dataset.label;
if (label) {
label += ': ';
}
if (context.parsed.x !== null) {
label += Math.abs(context.parsed.x) + ' %';
}
return label;
},
},
},
}
}
JS);
}
}
20 changes: 20 additions & 0 deletions app/Livewire/Embeds/TopCountiesEmbed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Embeds;

use App\Livewire\Pages\ElectionTurnouts;
use Illuminate\View\View;
use Livewire\Attributes\Layout;

class TopCountiesEmbed extends ElectionTurnouts
{
#[Layout('components.layouts.embed')]
public function render(): View
{
$this->seo('Topul județelor după prezență');

return view('livewire.embeds.top-counties');
}
}
20 changes: 20 additions & 0 deletions app/Livewire/Embeds/TopLocalitiesEmbed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Embeds;

use App\Livewire\Pages\ElectionTurnouts;
use Illuminate\View\View;
use Livewire\Attributes\Layout;

class TopLocalitiesEmbed extends ElectionTurnouts
{
#[Layout('components.layouts.embed')]
public function render(): View
{
$this->seo('Top 10 mari orașe din România');

return view('livewire.embeds.top-localities');
}
}
3 changes: 3 additions & 0 deletions resources/views/livewire/embeds/top-counties.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="grid gap-8">
<livewire:charts.top-counties-chart :election="$election" />
</div>
3 changes: 3 additions & 0 deletions resources/views/livewire/embeds/top-localities.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="grid gap-8">
<livewire:charts.top-localities-chart :election="$election" />
</div>
2 changes: 2 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
Route::get('/embed/candidati/{election:slug}', Embeds\CandidatesEmbed::class)->name('elections.embed.candidates');
Route::get('/embed/mediu/{election:slug}', Embeds\AreaEmbed::class)->name('elections.embed.area');
Route::get('/embed/demografic/{election:slug}', Embeds\DemographicEmbed::class)->name('elections.embed.demographic');
Route::get('/embed/top/judete/{election:slug}', Embeds\TopCountiesEmbed::class)->name('elections.embed.top-counties');
Route::get('/embed/top/orase/{election:slug}', Embeds\TopLocalitiesEmbed::class)->name('elections.embed.top-localities');

Route::get('/embed/articol/{article}', Embeds\ArticleEmbed::class)->name('articles.embed');

Expand Down

0 comments on commit 1c53901

Please sign in to comment.