feat: Add many-to-many relationship for Issue Management with Departments, including migration, Filament UI updates, and a new department issue chart.
This commit is contained in:
parent
89b4793667
commit
01db1c4794
@ -35,13 +35,13 @@ public function handle()
|
||||
'migrate:classification' => '🏷️ Migrating classifications & sub-classifications...',
|
||||
'migrate:theme' => '🎨 Migrating themes...',
|
||||
'migrate:media-monitoring' => '🖥️ Migrating media monitoring...',
|
||||
'migrate:issue-management' => '⚠️ Migrating issue management...',
|
||||
'migrate:company' => '🏭 Migrating companies & partner media...',
|
||||
'migrate:journalist' => '📰 Migrating journalists...',
|
||||
'migrate:news' => '🗞️ Migrating news...',
|
||||
'migrate:announcement' => '📢 Migrating announcements...',
|
||||
'migrate:cooperation' => '🤝 Migrating cooperations, proposals & reports...',
|
||||
'migrate:content-recap' => '📊 Migrating content recaps...',
|
||||
'migrate:issue-management' => '⚠️ Migrating issue management...',
|
||||
];
|
||||
|
||||
foreach ($commands as $command => $description) {
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Enums\IssueSentiment;
|
||||
use App\Models\Department;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateIssueManagementCommand extends Command
|
||||
{
|
||||
@ -57,6 +60,38 @@ public function handle()
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
|
||||
// Handle Departments (Legacy 'agency' field is string, can be multiple separated by comma)
|
||||
$agencyString = trim($legacy->agency);
|
||||
if (! empty($agencyString)) {
|
||||
$agencyNames = explode(',', $agencyString);
|
||||
|
||||
foreach ($agencyNames as $agencyName) {
|
||||
$agencyName = trim($agencyName);
|
||||
if (empty($agencyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$department = Department::firstOrCreate(
|
||||
['name' => $agencyName],
|
||||
[
|
||||
'alias' => Str::limit($agencyName, 20, ''),
|
||||
'is_active' => IsActive::ACTIVE,
|
||||
]
|
||||
);
|
||||
|
||||
DB::table('department_issue_management')->updateOrInsert(
|
||||
[
|
||||
'department_id' => $department->id,
|
||||
'issue_management_id' => $legacy->id,
|
||||
],
|
||||
[
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
|
||||
@ -32,6 +32,14 @@ public static function configure(Schema $schema): Schema
|
||||
->autofocus()
|
||||
->helperText('Jika judul berita dari monitoring media tidak ada di opsi, silakan lakukan pencarian.'),
|
||||
|
||||
Select::make('departments')
|
||||
->label('Perangkat Daerah')
|
||||
->relationship('departments', 'name', fn (Builder $query): Builder => $query->active())
|
||||
->searchable()
|
||||
->preload()
|
||||
->multiple()
|
||||
->required(),
|
||||
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
Select::make('location_id')
|
||||
|
||||
@ -50,6 +50,12 @@ public static function configure(Table $table): Table
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('departments.name')
|
||||
->label('Perangkat Daerah')
|
||||
->badge()
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('location.name')
|
||||
->label('Lokus')
|
||||
->searchable()
|
||||
@ -101,6 +107,14 @@ public static function configure(Table $table): Table
|
||||
->preload()
|
||||
->native(false),
|
||||
|
||||
SelectFilter::make('departments')
|
||||
->label('Perangkat Daerah')
|
||||
->relationship('departments', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->multiple()
|
||||
->native(false),
|
||||
|
||||
SelectFilter::make('classification')
|
||||
->label('Klasifikasi')
|
||||
->relationship('classification', 'name')
|
||||
|
||||
71
app/Filament/Widgets/Charts/IssueDepartmentChart.php
Normal file
71
app/Filament/Widgets/Charts/IssueDepartmentChart.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets\Charts;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Models\IssueManagement;
|
||||
use BezhanSalleh\FilamentShield\Traits\HasWidgetShield;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
use Filament\Widgets\Concerns\InteractsWithPageFilters;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class IssueDepartmentChart extends ChartWidget
|
||||
{
|
||||
use HasWidgetShield, InteractsWithPageFilters;
|
||||
|
||||
protected ?string $heading = 'Sebaran Isu Berdasarkan Perangkat Daerah';
|
||||
|
||||
protected static ?int $sort = 16;
|
||||
|
||||
protected int|string|array $columnSpan = 1;
|
||||
|
||||
protected ?string $pollingInterval = null;
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$startDate = $this->filters['startDate'] ?? null;
|
||||
$endDate = $this->filters['endDate'] ?? null;
|
||||
|
||||
$issueTable = (new IssueManagement)->getTable();
|
||||
$departmentTable = (new Department)->getTable();
|
||||
$pivotTable = 'department_issue_management';
|
||||
|
||||
$data = IssueManagement::query()
|
||||
->join($pivotTable, "{$issueTable}.id", '=', "{$pivotTable}.issue_management_id")
|
||||
->join($departmentTable, "{$pivotTable}.department_id", '=', "{$departmentTable}.id")
|
||||
->when($startDate, fn ($q) => $q->whereDate("{$issueTable}.created_at", '>=', $startDate))
|
||||
->when($endDate, fn ($q) => $q->whereDate("{$issueTable}.created_at", '<=', $endDate))
|
||||
->select("{$departmentTable}.name", DB::raw('count(*) as total'))
|
||||
->groupBy("{$departmentTable}.id", "{$departmentTable}.name")
|
||||
->orderByDesc('total')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Jumlah Isu',
|
||||
'data' => $data->pluck('total')->toArray(),
|
||||
'backgroundColor' => [
|
||||
'#10B981',
|
||||
'#3B82F6',
|
||||
'#F59E0B',
|
||||
'#EC4899',
|
||||
'#6366F1',
|
||||
'#8B5CF6',
|
||||
'#F43F5E',
|
||||
'#06B6D4',
|
||||
'#14B8A6',
|
||||
'#F97316',
|
||||
],
|
||||
],
|
||||
],
|
||||
'labels' => $data->pluck('name')->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Swindon\FilamentHashids\Traits\HasHashid;
|
||||
|
||||
@ -20,4 +23,15 @@ protected function casts(): array
|
||||
'is_active' => IsActive::class,
|
||||
];
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function active(Builder $query): void
|
||||
{
|
||||
$query->where('is_active', IsActive::ACTIVE);
|
||||
}
|
||||
|
||||
public function issueManagements(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(IssueManagement::class, 'department_issue_management');
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Swindon\FilamentHashids\Traits\HasHashid;
|
||||
|
||||
@ -52,4 +53,9 @@ public function subLocation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SubLocation::class);
|
||||
}
|
||||
|
||||
public function departments(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Department::class, 'department_issue_management');
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('department_issue_management', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('department_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('issue_management_id')->constrained('issue_management')->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('department_issue_management');
|
||||
}
|
||||
};
|
||||
@ -124,6 +124,8 @@ public function run(): void
|
||||
"Replicate:Department",
|
||||
"Reorder:Department",
|
||||
|
||||
"View:IssueDepartmentChart",
|
||||
|
||||
"View:IssueLocationChart",
|
||||
|
||||
"ViewAny:IssueManagement",
|
||||
@ -387,6 +389,8 @@ public function run(): void
|
||||
"Replicate:Department",
|
||||
"Reorder:Department",
|
||||
|
||||
"View:IssueDepartmentChart",
|
||||
|
||||
"View:IssueLocationChart",
|
||||
|
||||
"ViewAny:IssueManagement",
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
use App\Enums\IssueSentiment;
|
||||
use App\Models\Classification;
|
||||
use App\Models\Department;
|
||||
use App\Models\IssueManagement;
|
||||
use App\Models\Location;
|
||||
use App\Models\MediaMonitoring;
|
||||
@ -13,6 +14,7 @@
|
||||
$location = Location::factory()->create();
|
||||
$classification = Classification::factory()->create();
|
||||
$monitoring = MediaMonitoring::factory()->create();
|
||||
$department = Department::factory()->create();
|
||||
|
||||
$issue = IssueManagement::factory()->create([
|
||||
'location_id' => $location->id,
|
||||
@ -21,7 +23,10 @@
|
||||
'issue' => IssueSentiment::POSITIVE,
|
||||
]);
|
||||
|
||||
$issue->departments()->attach($department->id);
|
||||
|
||||
expect($issue->issue)->toBe(IssueSentiment::POSITIVE);
|
||||
expect($issue->location->id)->toBe($location->id);
|
||||
expect($issue->mediaMonitoring->id)->toBe($monitoring->id);
|
||||
expect($issue->departments->first()->id)->toBe($department->id);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user