feat: Add a console command to simulate cooperation time, improve date validation, and refine UI texts and report creation conditions.

This commit is contained in:
Yoga Pangestu 2026-01-19 08:23:56 +07:00
parent 416905af9f
commit ad7789dd3f
10 changed files with 161 additions and 15 deletions

View File

@ -0,0 +1,130 @@
<?php
namespace App\Console\Commands;
use App\Enums\ApprovalStatus;
use App\Enums\CooperationStatus;
use App\Models\Cooperation;
use App\Models\CooperationMedia;
use Illuminate\Console\Command;
class SimulateCooperationTime extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'cooperation:simulate-time {id? : The ID of the cooperation}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Simulate time passing for a specific Cooperation by moving dates to the past (Local only)';
/**
* Execute the console command.
*/
public function handle()
{
if (! app()->isLocal()) {
$this->error('This command can only be run in local environment.');
return Command::FAILURE;
}
$cooperationId = $this->argument('id');
// Interactive Selection if ID not provided
if (! $cooperationId) {
$cooperations = Cooperation::whereIn('status', [
CooperationStatus::PENDING,
CooperationStatus::ASSIGNMENT,
])->get();
if ($cooperations->isEmpty()) {
$this->warn('No active cooperations (PENDING or ASSIGNMENT) found to simulate.');
return Command::SUCCESS;
}
$choices = [];
foreach ($cooperations as $c) {
// Handle enum value safely
$status = $c->status instanceof \UnitEnum ? $c->status->value : $c->status;
$choices[$c->id] = "[ID: {$c->id}] {$c->title} ({$status})";
}
$choice = $this->choice(
'Which cooperation do you want to fast-forward?',
$choices
);
// Retrieve the ID associated with the choice
$cooperationId = array_search($choice, $choices);
}
$cooperation = Cooperation::find($cooperationId);
if (! $cooperation) {
$this->error("Cooperation with ID {$cooperationId} not found.");
return Command::FAILURE;
}
$this->info("Simulating time passage for: {$cooperation->title}");
if ($cooperation->status === CooperationStatus::PENDING) {
// 1. Move dates to past
$cooperation->update([
'initial_submission_date' => now()->subDays(8),
'final_submission_date' => now()->subDay(),
]);
$this->info('Moved dates to the past (Final Submission: Yesterday).');
// 2. Reject unresponsive media
$rejectedCount = CooperationMedia::where('cooperation_id', $cooperation->id)
->pending()
->update(['status' => ApprovalStatus::REJECTED]);
if ($rejectedCount > 0) {
$this->info("Rejected {$rejectedCount} media that didn't respond.");
}
// 3. Update Status
$cooperation->update(['status' => CooperationStatus::ASSIGNMENT]);
$this->info('Status transitioned to ASSIGNMENT.');
} elseif ($cooperation->status === CooperationStatus::ASSIGNMENT) {
if ($cooperation->taskAssignment) {
// 1. Move task dates to past
$cooperation->taskAssignment->update([
'start_date' => now()->subDays(8),
'end_date' => now()->subDay(),
]);
$this->info('Moved task dates to the past (Task End: Yesterday).');
// 2. Reject unresponsive media (assignments)
$rejectedCount = CooperationMedia::where('cooperation_id', $cooperation->id)
->pending()
->update(['status' => ApprovalStatus::REJECTED]);
if ($rejectedCount > 0) {
$this->info("Rejected {$rejectedCount} media that didn't respond to assignment.");
}
// 3. Update Status
$cooperation->update(['status' => CooperationStatus::VERIFICATION]);
$this->info('Status transitioned to VERIFICATION.');
} else {
$this->error('This ASSIGNMENT cooperation has no Task Assignment yet.');
}
} else {
$status = $cooperation->status instanceof \UnitEnum ? $cooperation->status->value : $cooperation->status;
$this->warn("Cooperation status '{$status}' is not supported for simulation. Only PENDING and ASSIGNMENT are supported.");
}
return Command::SUCCESS;
}
}

View File

@ -25,7 +25,7 @@ protected function setUp(): void
->schema([
Textarea::make('reason')
->label('Alasan Penolakan')
->placeholder('...')
->placeholder('Jelaskan alasan pengajuan ditolak...')
->required(),
])
->action(function (CooperationProposal $record, array $data): void {

View File

@ -26,7 +26,7 @@ protected function setUp(): void
->schema([
Textarea::make('reason')
->label('Alasan Penolakan')
->placeholder('Jelaskan apa yang perlu diperbaiki...')
->placeholder('Jelaskan alasan pengajuan ditolak...')
->required(),
])
->action(function (Report $record, array $data): void {

View File

@ -44,7 +44,11 @@ public static function canViewForRecord(Model $ownerRecord, string $pageClass):
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
{
return $ownerRecord->proposal()->count();
if (auth()->user()->hasRole('Perusahaan')) {
return null;
}
return (string) $ownerRecord->proposal()->count();
}
public function table(Table $table): Table
@ -103,7 +107,18 @@ public function table(Table $table): Table
->success()
),
]),
]);
])
->modifyQueryUsing(function ($query) {
$user = auth()->user();
if (! $user->hasRole('Perusahaan')) {
return $query;
}
return $query->whereHas('partnerMedia.company', function ($q) use ($user) {
$q->where('user_id', $user->id);
});
});
}
public function infolist(Schema $infolist): Schema

View File

@ -182,6 +182,8 @@ public function table(Table $table): Table
}
return $this->getOwnerRecord()->status === CooperationStatus::ASSIGNMENT
&& $taskAssignment->start_date->toDateString() <= now()->toDateString()
&& $taskAssignment->end_date->toDateString() >= now()->toDateString()
&& $taskAssignment->reports()
->where('status', '!=', ApprovalStatus::REJECTED)
->count() < $taskAssignment->report_amount;

View File

@ -37,11 +37,6 @@ public static function canViewForRecord(Model $ownerRecord, string $pageClass):
return true;
}
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
{
return $ownerRecord->taskAssignment()->count();
}
public function table(Table $table): Table
{
return $table

View File

@ -12,6 +12,7 @@
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
@ -39,15 +40,18 @@ public static function configure(Schema $schema): Schema
->placeholder(fn (): string => now()->translatedFormat('l, d F Y'))
->native(false)
->displayFormat('l, d F Y')
->required(),
->required()
->minDate(today())
->reactive(),
DatePicker::make('final_submission_date')
->label('Tanggal Pengajuan Akhir')
->placeholder(fn (): string => now()->addDays(30)->translatedFormat('l, d F Y'))
->placeholder(fn (): string => now()->addDays(7)->translatedFormat('l, d F Y'))
->native(false)
->displayFormat('l, d F Y')
->required()
->after('initial_submission_date'),
->afterOrEqual('initial_submission_date')
->minDate(fn (Get $get) => $get('initial_submission_date')),
]),
Select::make('partner_media_ids')

View File

@ -18,7 +18,7 @@ protected function casts(): array
{
return [
'amount' => 'integer',
'payment_date' => 'datetime',
'payment_date' => 'date',
];
}

View File

@ -14,7 +14,7 @@ public function up(): void
{
Schema::create('cooperation_payments', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(CooperationMedia::class)->constrained()->cascadeOnDelete();
$table->foreignIdFor(CooperationMedia::class, 'cooperation_media_id')->constrained()->cascadeOnDelete();
$table->unsignedInteger('amount');
$table->text('description')->nullable();
$table->date('payment_date')->useCurrent();

View File

@ -19,7 +19,7 @@ public function run(): void
'title' => 'Kerja Sama Media '.now()->format('Y'),
'description' => 'Kerja sama media untuk penyebarluasan informasi publik.',
'initial_submission_date' => now()->subDay(),
'final_submission_date' => now(),
'final_submission_date' => now()->addDay(),
'status' => CooperationStatus::PENDING,
]);