388 lines
14 KiB
PHP
388 lines
14 KiB
PHP
<?php
|
||
|
||
namespace App\Filament\Widgets;
|
||
|
||
use App\Enums\OverrideScope;
|
||
use App\Models\ProfitCenterSupplierSchedule;
|
||
use App\Models\DeliveryCalendarOverride;
|
||
use App\Models\WorkCalendar;
|
||
use App\Enums\WorkDayType;
|
||
use App\Services\DeliveryCalendarService;
|
||
use App\Services\WorkCalendarService;
|
||
use Illuminate\Support\Carbon;
|
||
use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Filament\Actions\Action;
|
||
use Filament\Forms\Components\Hidden;
|
||
use Filament\Forms\Components\Placeholder;
|
||
use Filament\Forms\Components\Toggle;
|
||
use Filament\Forms\Components\TextInput;
|
||
use Livewire\Attributes\On;
|
||
|
||
class DeliveryCalendarWidget extends FullCalendarWidget
|
||
{
|
||
/**
|
||
* @var ProfitCenterSupplierSchedule|null
|
||
*/
|
||
public Model | int | string | null $record = null;
|
||
|
||
public ?int $supplierId = null;
|
||
|
||
public ?int $profitCenterId = null;
|
||
|
||
/**
|
||
* Inaktív (nem választható) napok listája.
|
||
*
|
||
* @var array<string>
|
||
*/
|
||
protected array $inactiveDates = [];
|
||
|
||
public function mount(): void
|
||
{
|
||
$this->loadRecordFromFilter();
|
||
}
|
||
|
||
public function updatedSupplierId(): void
|
||
{
|
||
$this->loadRecordFromFilter();
|
||
$this->refreshRecords();
|
||
}
|
||
|
||
public function updatedProfitCenterId(): void
|
||
{
|
||
$this->loadRecordFromFilter();
|
||
$this->refreshRecords();
|
||
}
|
||
|
||
#[On('calendar-filter-changed')]
|
||
public function onFilterChanged(?int $supplierId, ?int $profitCenterId): void
|
||
{
|
||
$this->supplierId = $supplierId;
|
||
$this->profitCenterId = $profitCenterId;
|
||
$this->record = null;
|
||
$this->loadRecordFromFilter();
|
||
$this->refreshRecords();
|
||
}
|
||
|
||
protected function loadRecordFromFilter(): void
|
||
{
|
||
if ($this->record instanceof ProfitCenterSupplierSchedule) {
|
||
return;
|
||
}
|
||
|
||
if ($this->supplierId && $this->profitCenterId) {
|
||
$this->record = ProfitCenterSupplierSchedule::where('supplier_id', $this->supplierId)
|
||
->where('profit_center_id', $this->profitCenterId)
|
||
->first();
|
||
return;
|
||
}
|
||
|
||
if (!$this->record) {
|
||
$this->record = ProfitCenterSupplierSchedule::first();
|
||
}
|
||
}
|
||
|
||
public function fetchEvents(array $info): array
|
||
{
|
||
if (!($this->record instanceof ProfitCenterSupplierSchedule)) {
|
||
$this->loadRecordFromFilter();
|
||
}
|
||
|
||
if (!$this->record) {
|
||
$this->dispatch('calendar-events-done');
|
||
return $this->getDemoEvents($info);
|
||
}
|
||
|
||
/** @var DeliveryCalendarService $service */
|
||
$service = app(DeliveryCalendarService::class);
|
||
/** @var WorkCalendarService $workService */
|
||
$workService = app(WorkCalendarService::class);
|
||
|
||
$start = Carbon::parse($info['start']);
|
||
$end = Carbon::parse($info['end']);
|
||
|
||
$events = [];
|
||
|
||
// 1. Szállítási napok – zöld háttér
|
||
$deliveryDates = $service->getAvailableDeliveryDates(
|
||
$this->record->profitCenter,
|
||
$this->record->supplier,
|
||
$start,
|
||
$end
|
||
);
|
||
|
||
foreach ($deliveryDates as $date) {
|
||
$events[] = [
|
||
'id' => 'delivery-' . $date->format('Y-m-d'),
|
||
'title' => '',
|
||
'start' => $date->format('Y-m-d'),
|
||
'end' => $date->addDay()->format('Y-m-d'),
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#4CAF50',
|
||
'extendedProps' => ['type' => 'delivery'],
|
||
];
|
||
}
|
||
|
||
// 2. Munkaszüneti napok – piros háttér
|
||
$workCalendarEntries = $workService->getCalendarEntries($start, $end);
|
||
foreach ($workCalendarEntries as $entry) {
|
||
if ($entry->type === WorkDayType::Holiday) {
|
||
$events[] = [
|
||
'id' => 'holiday-' . $entry->date->format('Y-m-d'),
|
||
'title' => '',
|
||
'start' => $entry->date->format('Y-m-d'),
|
||
'end' => $entry->date->copy()->addDay()->format('Y-m-d'),
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#EF4444',
|
||
'extendedProps' => ['type' => 'holiday'],
|
||
];
|
||
} elseif ($entry->type === WorkDayType::WorkingDay) {
|
||
// 3. Áthelyezett munkanapok – narancssárga háttér
|
||
$events[] = [
|
||
'id' => 'transferred-' . $entry->date->format('Y-m-d'),
|
||
'title' => '',
|
||
'start' => $entry->date->format('Y-m-d'),
|
||
'end' => $entry->date->copy()->addDay()->format('Y-m-d'),
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#F59E0B',
|
||
'extendedProps' => ['type' => 'transferred'],
|
||
];
|
||
}
|
||
}
|
||
|
||
// 4. Hétvégék – szürke háttér (inaktív napok)
|
||
$current = $start->copy();
|
||
while ($current->lte($end)) {
|
||
if ($current->isWeekend()) {
|
||
$events[] = [
|
||
'id' => 'weekend-' . $current->format('Y-m-d'),
|
||
'title' => '',
|
||
'start' => $current->format('Y-m-d'),
|
||
'end' => $current->copy()->addDay()->format('Y-m-d'),
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#E5E7EB',
|
||
'extendedProps' => ['type' => 'inactive'],
|
||
];
|
||
}
|
||
$current->addDay();
|
||
}
|
||
|
||
$this->dispatch('calendar-events-done');
|
||
return $events;
|
||
}
|
||
|
||
/**
|
||
* Demo események generálása, ha nincs valós adat.
|
||
*
|
||
* @param array{start: string, end: string} $info
|
||
* @return array<int, array<string, mixed>>
|
||
*/
|
||
protected function getDemoEvents(array $info): array
|
||
{
|
||
$start = Carbon::parse($info['start']);
|
||
$end = Carbon::parse($info['end']);
|
||
$events = [];
|
||
|
||
$current = $start->copy();
|
||
while ($current->lte($end)) {
|
||
$dayOfWeek = $current->dayOfWeek;
|
||
$dayOfMonth = $current->day;
|
||
$dateStr = $current->format('Y-m-d');
|
||
$nextDateStr = $current->copy()->addDay()->format('Y-m-d');
|
||
|
||
if ($current->isWeekend()) {
|
||
// Hétvégék – szürke háttér (inaktív)
|
||
$events[] = [
|
||
'id' => 'weekend-' . $dateStr,
|
||
'title' => '',
|
||
'start' => $dateStr,
|
||
'end' => $nextDateStr,
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#E5E7EB',
|
||
'extendedProps' => ['type' => 'inactive'],
|
||
];
|
||
} elseif ($dayOfWeek === Carbon::MONDAY || $dayOfWeek === Carbon::WEDNESDAY || $dayOfWeek === Carbon::FRIDAY) {
|
||
// Hétfő, szerda, péntek – zöld háttér (szállítási napok)
|
||
$events[] = [
|
||
'id' => 'delivery-' . $dateStr,
|
||
'title' => '',
|
||
'start' => $dateStr,
|
||
'end' => $nextDateStr,
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#4CAF50',
|
||
'extendedProps' => ['type' => 'delivery'],
|
||
];
|
||
}
|
||
|
||
// Néhány speciális nap bemutatóként
|
||
if ($dayOfMonth === 15) {
|
||
// Minden hónap 15-e – narancssárga (áthelyezett munkanap)
|
||
$events[] = [
|
||
'id' => 'special-orange-' . $dateStr,
|
||
'title' => '',
|
||
'start' => $dateStr,
|
||
'end' => $nextDateStr,
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#F59E0B',
|
||
'extendedProps' => ['type' => 'transferred'],
|
||
];
|
||
}
|
||
|
||
if ($dayOfMonth === 1) {
|
||
// Minden hónap 1-je – piros (ünnepnap)
|
||
$events[] = [
|
||
'id' => 'special-red-' . $dateStr,
|
||
'title' => '',
|
||
'start' => $dateStr,
|
||
'end' => $nextDateStr,
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#EF4444',
|
||
'extendedProps' => ['type' => 'holiday'],
|
||
];
|
||
}
|
||
|
||
if ($dayOfMonth === 10 || $dayOfMonth === 20) {
|
||
// 10-e és 20-a – kék (megrendelési határidő)
|
||
$events[] = [
|
||
'id' => 'special-blue-' . $dateStr,
|
||
'title' => '',
|
||
'start' => $dateStr,
|
||
'end' => $nextDateStr,
|
||
'allDay' => true,
|
||
'display' => 'background',
|
||
'backgroundColor' => '#3B82F6',
|
||
'extendedProps' => ['type' => 'deadline'],
|
||
];
|
||
}
|
||
|
||
$current->addDay();
|
||
}
|
||
|
||
return $events;
|
||
}
|
||
|
||
public function onDateSelect(string $start, ?string $end, bool $allDay, ?array $view, ?array $resource): void
|
||
{
|
||
$this->mountAction('manageOverride', [
|
||
'date' => Carbon::parse($start)->format('Y-m-d'),
|
||
]);
|
||
}
|
||
|
||
public function onEventClick(array $event): void
|
||
{
|
||
$date = data_get($event, 'start');
|
||
|
||
if (!$date) {
|
||
return;
|
||
}
|
||
|
||
$this->mountAction('manageOverride', [
|
||
'date' => Carbon::parse($date)->format('Y-m-d'),
|
||
]);
|
||
}
|
||
|
||
protected function headerActions(): array
|
||
{
|
||
return [
|
||
Action::make('manageOverride')
|
||
->label('Felülbírálás kezelése')
|
||
->modalHeading(fn (array $arguments) => "Szállítás felülbírálása" . (isset($arguments['date']) ? ": " . $arguments['date'] : ""))
|
||
->form(fn (array $arguments) => [
|
||
Placeholder::make('info')
|
||
->hiddenLabel()
|
||
->content('A háttérben látható naptárban egyszerre több hónapot is áttekinthet. Itt egy konkrét nap módosítását végezheti el.'),
|
||
\Filament\Forms\Components\DatePicker::make('date')
|
||
->label('Dátum')
|
||
->required()
|
||
->default($arguments['date'] ?? null)
|
||
->disabled(fn () => isset($arguments['date']))
|
||
->dehydrated(),
|
||
Toggle::make('is_delivery_day')
|
||
->label('Szállítási nap?')
|
||
->default(function () use ($arguments) {
|
||
$date = $arguments['date'] ?? null;
|
||
if (!$date) {
|
||
return true;
|
||
}
|
||
|
||
$override = DeliveryCalendarOverride::where('supplier_id', $this->record->supplier_id)
|
||
->whereDate('date', $date)
|
||
->where('override_scope', OverrideScope::ProfitCenter->value)
|
||
->where('profit_center_id', $this->record->profit_center_id)
|
||
->first();
|
||
|
||
if ($override) {
|
||
return $override->is_delivery_day;
|
||
}
|
||
|
||
return app(DeliveryCalendarService::class)->isDeliveryDay(
|
||
$this->record->profitCenter,
|
||
$this->record->supplier,
|
||
Carbon::parse($date)
|
||
);
|
||
}),
|
||
TextInput::make('description')
|
||
->label('Indoklás / Megjegyzés')
|
||
->default(fn () => isset($arguments['date'])
|
||
? DeliveryCalendarOverride::where('supplier_id', $this->record->supplier_id)
|
||
->whereDate('date', $arguments['date'])
|
||
->where('override_scope', OverrideScope::ProfitCenter->value)
|
||
->where('profit_center_id', $this->record->profit_center_id)
|
||
->first()?->description
|
||
: null
|
||
),
|
||
])
|
||
->action(function (array $data) {
|
||
DeliveryCalendarOverride::updateOrCreate(
|
||
[
|
||
'supplier_id' => $this->record->supplier_id,
|
||
'override_scope' => OverrideScope::ProfitCenter->value,
|
||
'profit_center_id' => $this->record->profit_center_id,
|
||
'date' => $data['date'],
|
||
],
|
||
[
|
||
'is_delivery_day' => (bool) $data['is_delivery_day'],
|
||
'description' => $data['description'],
|
||
]
|
||
);
|
||
|
||
$this->refreshRecords();
|
||
}),
|
||
];
|
||
}
|
||
|
||
public function config(): array
|
||
{
|
||
return [
|
||
'initialView' => 'multiMonthYear',
|
||
'views' => [
|
||
'multiMonthYear' => [
|
||
'duration' => ['months' => 6],
|
||
],
|
||
],
|
||
'multiMonthMaxColumns' => 6,
|
||
'headerToolbar' => [
|
||
'left' => 'prev,next today',
|
||
'center' => 'title',
|
||
'right' => 'multiMonthYear',
|
||
],
|
||
'locale' => 'hu',
|
||
'firstDay' => 1,
|
||
'height' => 'auto',
|
||
'contentHeight' => 'auto',
|
||
'selectable' => true,
|
||
'editable' => false,
|
||
'dayMaxEvents' => false,
|
||
'fixedWeekCount' => false,
|
||
];
|
||
}
|
||
}
|