124 lines
3.6 KiB
PHP
124 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Widgets\Charts;
|
|
|
|
use App\Enums\IssueSentiment;
|
|
use App\Models\IssueManagement;
|
|
use BezhanSalleh\FilamentShield\Traits\HasWidgetShield;
|
|
use Filament\Widgets\ChartWidget;
|
|
use Filament\Widgets\Concerns\InteractsWithPageFilters;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class SentimentTrendChart extends ChartWidget
|
|
{
|
|
use HasWidgetShield, InteractsWithPageFilters;
|
|
|
|
protected ?string $heading = 'Dinamika Sentimen Isu (Area)';
|
|
|
|
protected static ?int $sort = 18;
|
|
|
|
protected int|string|array $columnSpan = 1;
|
|
|
|
protected ?string $pollingInterval = null;
|
|
|
|
protected function getData(): array
|
|
{
|
|
$startDate = $this->filters['startDate'] ?? Carbon::now()->subDays(30);
|
|
$endDate = $this->filters['endDate'] ?? Carbon::now();
|
|
|
|
$startDate = Carbon::parse($startDate)->startOfDay();
|
|
$endDate = Carbon::parse($endDate)->endOfDay();
|
|
|
|
$query = IssueManagement::query()
|
|
->select(
|
|
DB::raw('DATE(created_at) as date'),
|
|
'issue',
|
|
DB::raw('count(*) as total')
|
|
)
|
|
->whereBetween('created_at', [$startDate, $endDate])
|
|
->groupBy('date', 'issue')
|
|
->orderBy('date')
|
|
->get();
|
|
|
|
$labels = [];
|
|
$current = clone $startDate;
|
|
while ($current <= $endDate) {
|
|
$labels[] = $current->translatedFormat('d M');
|
|
$current->addDay();
|
|
}
|
|
|
|
$datasets = [];
|
|
foreach (IssueSentiment::cases() as $sentiment) {
|
|
$data = [];
|
|
$current = clone $startDate;
|
|
while ($current <= $endDate) {
|
|
$dateStr = $current->toDateString();
|
|
$count = $query->where('date', $dateStr)
|
|
->where('issue', $sentiment->value)
|
|
->first();
|
|
$data[] = $count ? $count->total : 0;
|
|
$current->addDay();
|
|
}
|
|
|
|
$datasets[] = [
|
|
'label' => $sentiment->getLabel(),
|
|
'data' => $data,
|
|
'fill' => 'origin',
|
|
'backgroundColor' => $this->getHexColorRGBA($sentiment, 0.2),
|
|
'borderColor' => $this->getHexColor($sentiment),
|
|
'tension' => 0.4,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'datasets' => $datasets,
|
|
'labels' => $labels,
|
|
];
|
|
}
|
|
|
|
private function getHexColor(IssueSentiment $sentiment): string
|
|
{
|
|
return match ($sentiment) {
|
|
IssueSentiment::POSITIVE => '#10B981',
|
|
IssueSentiment::NEGATIVE => '#EF4444',
|
|
IssueSentiment::NEUTRAL => '#6B7280',
|
|
IssueSentiment::CRISIS => '#F97316',
|
|
};
|
|
}
|
|
|
|
private function getHexColorRGBA(IssueSentiment $sentiment, float $opacity): string
|
|
{
|
|
return match ($sentiment) {
|
|
IssueSentiment::POSITIVE => "rgba(16, 185, 129, {$opacity})",
|
|
IssueSentiment::NEGATIVE => "rgba(239, 68, 68, {$opacity})",
|
|
IssueSentiment::NEUTRAL => "rgba(107, 114, 128, {$opacity})",
|
|
IssueSentiment::CRISIS => "rgba(249, 115, 22, {$opacity})",
|
|
};
|
|
}
|
|
|
|
protected function getType(): string
|
|
{
|
|
return 'line';
|
|
}
|
|
|
|
protected function getOptions(): array
|
|
{
|
|
return [
|
|
'plugins' => [
|
|
'legend' => [
|
|
'display' => true,
|
|
],
|
|
],
|
|
'scales' => [
|
|
'y' => [
|
|
'beginAtZero' => true,
|
|
'ticks' => [
|
|
'stepSize' => 1,
|
|
],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
}
|