app/app/Livewire/Schedule/ScheduleCalendar.php

433 lines
13 KiB
PHP

<?php
namespace App\Livewire\Schedule;
use App\Models\Dish;
use App\Models\Schedule;
use App\Models\ScheduledUserDish;
use App\Models\User;
use App\Models\UserDish;
use Carbon\Carbon;
use DishPlanner\Schedule\Services\ScheduleCalendarService;
use DishPlanner\ScheduledUserDish\Actions\DeleteScheduledUserDishForDateAction;
use DishPlanner\ScheduledUserDish\Actions\SkipScheduledUserDishForDateAction;
use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Log;
use Livewire\Component;
class ScheduleCalendar extends Component
{
public $currentMonth;
public $currentYear;
public $calendarDays = [];
public $showRegenerateModal = false;
public $regenerateDate = null;
public $regenerateUserId = null;
// Edit dish modal
public $showEditDishModal = false;
public $editDate = null;
public $editUserId = null;
public $selectedDishId = null;
public $availableDishes = [];
// Add dish modal
public $showAddDishModal = false;
public $addDate = null;
public $addUserIds = [];
public $addSelectedDishId = null;
public $addAvailableUsers = [];
public $addAvailableDishes = [];
public function mount(): void
{
$this->currentMonth = now()->month;
$this->currentYear = now()->year;
$this->loadCalendar();
}
protected $listeners = ['schedule-generated' => 'refreshCalendar'];
public function render(): View
{
return view('livewire.schedule.schedule-calendar');
}
public function refreshCalendar(): void
{
$this->loadCalendar();
}
public function loadCalendar(): void
{
$service = new ScheduleCalendarService();
$this->calendarDays = $service->getCalendarDays(
auth()->user(),
$this->currentMonth,
$this->currentYear
);
}
public function previousMonth(): void
{
if ($this->currentMonth === 1) {
$this->currentMonth = 12;
$this->currentYear--;
} else {
$this->currentMonth--;
}
$this->loadCalendar();
}
public function nextMonth(): void
{
if ($this->currentMonth === 12) {
$this->currentMonth = 1;
$this->currentYear++;
} else {
$this->currentMonth++;
}
$this->loadCalendar();
}
public function regenerateForUserDate($date, $userId): void
{
if (!$this->authorizeUser($userId)) {
session()->flash('error', 'Unauthorized action.');
return;
}
$this->regenerateDate = $date;
$this->regenerateUserId = $userId;
$this->showRegenerateModal = true;
}
public function confirmRegenerate(): void
{
try {
if (!$this->authorizeUser($this->regenerateUserId)) {
session()->flash('error', 'Unauthorized action.');
return;
}
$action = new DeleteScheduledUserDishForDateAction();
$action->execute(
auth()->user(),
Carbon::parse($this->regenerateDate),
$this->regenerateUserId
);
$this->showRegenerateModal = false;
$this->loadCalendar();
session()->flash('success', 'Schedule regenerated for the selected date!');
} catch (Exception $e) {
Log::error('Schedule regeneration failed', ['exception' => $e, 'date' => $this->regenerateDate]);
session()->flash('error', 'Unable to regenerate schedule. Please try again.');
}
}
public function skipDay($date, $userId): void
{
try {
if (!$this->authorizeUser($userId)) {
session()->flash('error', 'Unauthorized action.');
return;
}
$action = new SkipScheduledUserDishForDateAction();
$action->execute(
auth()->user(),
Carbon::parse($date),
$userId
);
$this->loadCalendar();
session()->flash('success', 'Day skipped successfully!');
} catch (Exception $e) {
Log::error('Skip day failed', ['exception' => $e, 'date' => $date, 'userId' => $userId]);
session()->flash('error', 'Unable to skip day. Please try again.');
}
}
private function authorizeUser(int $userId): bool
{
$user = User::find($userId);
return $user && $user->planner_id === auth()->id();
}
public function cancel(): void
{
$this->showRegenerateModal = false;
$this->regenerateDate = null;
$this->regenerateUserId = null;
$this->showEditDishModal = false;
$this->editDate = null;
$this->editUserId = null;
$this->selectedDishId = null;
$this->availableDishes = [];
$this->showAddDishModal = false;
$this->addDate = null;
$this->addUserIds = [];
$this->addSelectedDishId = null;
$this->addAvailableUsers = [];
$this->addAvailableDishes = [];
}
public function removeDish($date, $userId): void
{
try {
if (!$this->authorizeUser($userId)) {
session()->flash('error', 'Unauthorized action.');
return;
}
$schedule = Schedule::where('planner_id', auth()->id())
->where('date', $date)
->first();
if ($schedule) {
ScheduledUserDish::where('schedule_id', $schedule->id)
->where('user_id', $userId)
->delete();
}
$this->loadCalendar();
session()->flash('success', 'Dish removed successfully!');
} catch (Exception $e) {
Log::error('Remove dish failed', ['exception' => $e, 'date' => $date, 'userId' => $userId]);
session()->flash('error', 'Unable to remove dish. Please try again.');
}
}
public function openAddDishModal($date): void
{
$this->addDate = $date;
// Load all users for this planner
$this->addAvailableUsers = User::where('planner_id', auth()->id())
->orderBy('name')
->get();
$this->addAvailableDishes = [];
$this->addUserIds = [];
$this->addSelectedDishId = null;
$this->showAddDishModal = true;
}
public function toggleAllUsers(): void
{
if (count($this->addUserIds) === count($this->addAvailableUsers)) {
$this->addUserIds = [];
} else {
$this->addUserIds = $this->addAvailableUsers->pluck('id')->map(fn($id) => (string) $id)->toArray();
}
$this->updateAvailableDishes();
}
public function updatedAddUserIds(): void
{
$this->updateAvailableDishes();
}
private function updateAvailableDishes(): void
{
if (empty($this->addUserIds)) {
$this->addAvailableDishes = [];
} else {
// Load dishes that ALL selected users have in common
$selectedCount = count($this->addUserIds);
$this->addAvailableDishes = Dish::whereHas('users', function ($query) {
$query->whereIn('users.id', $this->addUserIds);
}, '=', $selectedCount)->orderBy('name')->get();
}
$this->addSelectedDishId = null;
}
public function saveAddDish(): void
{
try {
if (empty($this->addUserIds)) {
session()->flash('error', 'Please select at least one user.');
return;
}
if (!$this->addSelectedDishId) {
session()->flash('error', 'Please select a dish.');
return;
}
// Find or create the schedule for this date
$schedule = Schedule::firstOrCreate(
[
'planner_id' => auth()->id(),
'date' => $this->addDate,
],
['is_skipped' => false]
);
$addedCount = 0;
$skippedCount = 0;
foreach ($this->addUserIds as $userId) {
if (!$this->authorizeUser((int) $userId)) {
$skippedCount++;
continue;
}
// Check if user already has a dish scheduled for this date
$existing = ScheduledUserDish::where('schedule_id', $schedule->id)
->where('user_id', $userId)
->first();
if ($existing) {
$skippedCount++;
continue;
}
// Find the UserDish for this user and dish
$userDish = UserDish::where('user_id', $userId)
->where('dish_id', $this->addSelectedDishId)
->first();
if (!$userDish) {
$skippedCount++;
continue;
}
// Create the scheduled user dish
ScheduledUserDish::create([
'schedule_id' => $schedule->id,
'user_id' => $userId,
'user_dish_id' => $userDish->id,
'is_skipped' => false,
]);
$addedCount++;
}
$this->closeAddDishModal();
$this->loadCalendar();
if ($addedCount > 0 && $skippedCount > 0) {
session()->flash('success', "Dish added for {$addedCount} user(s). {$skippedCount} user(s) skipped (already scheduled).");
} elseif ($addedCount > 0) {
session()->flash('success', "Dish added for {$addedCount} user(s)!");
} else {
session()->flash('error', 'No users could be scheduled. They may already have dishes for this date.');
}
} catch (Exception $e) {
Log::error('Add dish failed', ['exception' => $e]);
session()->flash('error', 'Unable to add dish. Please try again.');
}
}
private function closeAddDishModal(): void
{
$this->showAddDishModal = false;
$this->addDate = null;
$this->addUserIds = [];
$this->addSelectedDishId = null;
$this->addAvailableUsers = [];
$this->addAvailableDishes = [];
}
public function editDish($date, $userId): void
{
if (!$this->authorizeUser($userId)) {
session()->flash('error', 'Unauthorized action.');
return;
}
$this->editDate = $date;
$this->editUserId = $userId;
// Load dishes available for this user (via UserDish pivot)
$this->availableDishes = Dish::whereHas('users', function ($query) use ($userId) {
$query->where('users.id', $userId);
})->orderBy('name')->get();
// Get currently selected dish for this date/user if exists
$schedule = Schedule::where('planner_id', auth()->id())
->where('date', $date)
->first();
if ($schedule) {
$scheduledUserDish = ScheduledUserDish::where('schedule_id', $schedule->id)
->where('user_id', $userId)
->first();
if ($scheduledUserDish && $scheduledUserDish->userDish) {
$this->selectedDishId = $scheduledUserDish->userDish->dish_id;
}
}
$this->showEditDishModal = true;
}
public function saveDish(): void
{
try {
if (!$this->authorizeUser($this->editUserId)) {
session()->flash('error', 'Unauthorized action.');
return;
}
if (!$this->selectedDishId) {
session()->flash('error', 'Please select a dish.');
return;
}
// Find or create the schedule for this date
$schedule = Schedule::firstOrCreate(
[
'planner_id' => auth()->id(),
'date' => $this->editDate,
],
['is_skipped' => false]
);
// Find the UserDish for this user and dish
$userDish = UserDish::where('user_id', $this->editUserId)
->where('dish_id', $this->selectedDishId)
->first();
if (!$userDish) {
session()->flash('error', 'This dish is not assigned to this user.');
return;
}
// Update or create the scheduled user dish
ScheduledUserDish::updateOrCreate(
[
'schedule_id' => $schedule->id,
'user_id' => $this->editUserId,
],
[
'user_dish_id' => $userDish->id,
'is_skipped' => false,
]
);
$this->showEditDishModal = false;
$this->editDate = null;
$this->editUserId = null;
$this->selectedDishId = null;
$this->availableDishes = [];
$this->loadCalendar();
session()->flash('success', 'Dish updated successfully!');
} catch (Exception $e) {
Log::error('Save dish failed', ['exception' => $e]);
session()->flash('error', 'Unable to save dish. Please try again.');
}
}
public function getMonthNameProperty(): string
{
$service = new ScheduleCalendarService();
return $service->getMonthName($this->currentMonth, $this->currentYear);
}
}