bug - 14 - Fix create modal on calendar cell + add remove option for existing
This commit is contained in:
parent
d57af05974
commit
7412316746
3 changed files with 393 additions and 9 deletions
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
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;
|
||||
|
|
@ -21,6 +25,21 @@ class ScheduleCalendar extends Component
|
|||
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 $addUserId = null;
|
||||
public $addSelectedDishId = null;
|
||||
public $addAvailableUsers = [];
|
||||
public $addAvailableDishes = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->currentMonth = now()->month;
|
||||
|
|
@ -144,6 +163,233 @@ 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->addUserId = null;
|
||||
$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->addUserId = null;
|
||||
$this->addSelectedDishId = null;
|
||||
|
||||
$this->showAddDishModal = true;
|
||||
}
|
||||
|
||||
public function updatedAddUserId($value): void
|
||||
{
|
||||
if ($value) {
|
||||
// Load dishes available for selected user
|
||||
$this->addAvailableDishes = Dish::whereHas('users', function ($query) use ($value) {
|
||||
$query->where('users.id', $value);
|
||||
})->orderBy('name')->get();
|
||||
} else {
|
||||
$this->addAvailableDishes = [];
|
||||
}
|
||||
$this->addSelectedDishId = null;
|
||||
}
|
||||
|
||||
public function saveAddDish(): void
|
||||
{
|
||||
try {
|
||||
if (!$this->addUserId) {
|
||||
session()->flash('error', 'Please select a user.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->authorizeUser($this->addUserId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
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]
|
||||
);
|
||||
|
||||
// Check if user already has a dish scheduled for this date
|
||||
$existing = ScheduledUserDish::where('schedule_id', $schedule->id)
|
||||
->where('user_id', $this->addUserId)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
session()->flash('error', 'This user already has a dish scheduled for this date. Use Edit instead.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the UserDish for this user and dish
|
||||
$userDish = UserDish::where('user_id', $this->addUserId)
|
||||
->where('dish_id', $this->addSelectedDishId)
|
||||
->first();
|
||||
|
||||
if (!$userDish) {
|
||||
session()->flash('error', 'This dish is not assigned to this user.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the scheduled user dish
|
||||
ScheduledUserDish::create([
|
||||
'schedule_id' => $schedule->id,
|
||||
'user_id' => $this->addUserId,
|
||||
'user_dish_id' => $userDish->id,
|
||||
'is_skipped' => false,
|
||||
]);
|
||||
|
||||
$this->showAddDishModal = false;
|
||||
$this->addDate = null;
|
||||
$this->addUserId = null;
|
||||
$this->addSelectedDishId = null;
|
||||
$this->addAvailableUsers = [];
|
||||
$this->addAvailableDishes = [];
|
||||
|
||||
$this->loadCalendar();
|
||||
session()->flash('success', 'Dish added successfully!');
|
||||
} catch (Exception $e) {
|
||||
Log::error('Add dish failed', ['exception' => $e]);
|
||||
session()->flash('error', 'Unable to add dish. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public function up(): void
|
|||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('user_dish_id')->references('id')->on('user_dishes')->onDelete('cascade');
|
||||
|
||||
$table->unique(['schedule_id', 'user_dish_id']);
|
||||
$table->unique(['schedule_id', 'user_id']);
|
||||
$table->index('user_dish_id');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,15 @@ class="px-4 py-2 bg-gray-700 text-accent-blue rounded hover:bg-gray-600 transiti
|
|||
{{ $dayData['isToday'] ? 'border-2 border-accent-blue' : 'border-gray-600' }}">
|
||||
|
||||
@if($dayData['day'])
|
||||
<!-- Day number -->
|
||||
<div class="font-bold mb-2 {{ $dayData['isToday'] ? 'text-accent-blue' : 'text-gray-100' }}">
|
||||
{{ $dayData['day'] }}
|
||||
<!-- Day number and add button -->
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="font-bold {{ $dayData['isToday'] ? 'text-accent-blue' : 'text-gray-100' }}">
|
||||
{{ $dayData['day'] }}
|
||||
</span>
|
||||
<button wire:click="openAddDishModal('{{ $dayData['date']->format('Y-m-d') }}')"
|
||||
class="w-5 h-5 bg-gray-600 hover:bg-primary text-gray-300 hover:text-white rounded flex items-center justify-center text-sm transition-colors duration-200">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Scheduled dishes -->
|
||||
|
|
@ -76,14 +82,22 @@ class="text-white hover:text-gray-300">
|
|||
@click.away="showActions = false"
|
||||
x-cloak
|
||||
class="absolute bg-gray-700 border border-secondary rounded mt-4 ml-4 shadow-lg z-10">
|
||||
<button wire:click="editDish('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-3 py-1 text-xs hover:bg-gray-600 text-white">
|
||||
Edit
|
||||
</button>
|
||||
<button wire:click="regenerateForUserDate('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-3 py-1 text-xs hover:bg-gray-600 text-accent-blue">
|
||||
Regenerate
|
||||
</button>
|
||||
<button wire:click="skipDay('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-3 py-1 text-xs hover:bg-gray-600 text-danger">
|
||||
class="block w-full text-left px-3 py-1 text-xs hover:bg-gray-600 text-warning">
|
||||
Skip
|
||||
</button>
|
||||
<button wire:click="removeDish('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-3 py-1 text-xs hover:bg-gray-600 text-danger">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -113,6 +127,10 @@ class="block w-full text-left px-3 py-1 text-xs hover:bg-gray-600 text-danger">
|
|||
<span class="text-xs ml-2">(Today)</span>
|
||||
@endif
|
||||
</div>
|
||||
<button wire:click="openAddDishModal('{{ $dayData['date']->format('Y-m-d') }}')"
|
||||
class="w-7 h-7 bg-gray-600 hover:bg-primary text-gray-300 hover:text-white rounded flex items-center justify-center text-lg transition-colors duration-200">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Scheduled dishes -->
|
||||
|
|
@ -141,14 +159,22 @@ class="text-white hover:text-gray-300 p-1">
|
|||
@click.away="showActions = false"
|
||||
x-cloak
|
||||
class="absolute right-4 bg-gray-700 border border-secondary rounded shadow-lg z-10">
|
||||
<button wire:click="editDish('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-600 text-white">
|
||||
Edit
|
||||
</button>
|
||||
<button wire:click="regenerateForUserDate('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-600 text-accent-blue">
|
||||
Regenerate
|
||||
</button>
|
||||
<button wire:click="skipDay('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-600 text-danger">
|
||||
class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-600 text-warning">
|
||||
Skip
|
||||
</button>
|
||||
<button wire:click="removeDish('{{ $dayData['date']->format('Y-m-d') }}', {{ $scheduled->user->id }})"
|
||||
class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-600 text-danger">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -171,13 +197,13 @@ class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-600 text-danger">
|
|||
<p class="text-gray-100 mb-6">
|
||||
This will clear the selected day and allow for regeneration. Continue?
|
||||
</p>
|
||||
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button wire:click="cancel"
|
||||
<button wire:click="cancel"
|
||||
class="px-4 py-2 border-2 border-secondary text-gray-100 rounded hover:bg-gray-700 transition-colors duration-200">
|
||||
Cancel
|
||||
</button>
|
||||
<button wire:click="confirmRegenerate"
|
||||
<button wire:click="confirmRegenerate"
|
||||
class="px-4 py-2 bg-warning text-white rounded hover:bg-yellow-600 transition-colors duration-200">
|
||||
Regenerate
|
||||
</button>
|
||||
|
|
@ -186,6 +212,118 @@ class="px-4 py-2 bg-warning text-white rounded hover:bg-yellow-600 transition-co
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Edit Dish Modal -->
|
||||
@if($showEditDishModal)
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-gray-600 border-2 border-secondary rounded-lg p-6 w-full max-w-md mx-4">
|
||||
<h2 class="text-xl text-accent-blue mb-4">Edit Dish</h2>
|
||||
<p class="text-gray-300 text-sm mb-4">
|
||||
Choose a dish for <strong>{{ \App\Models\User::find($editUserId)?->name }}</strong> on {{ \Carbon\Carbon::parse($editDate)->format('M j, Y') }}
|
||||
</p>
|
||||
|
||||
@if(count($availableDishes) > 0)
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium mb-2">Dish</label>
|
||||
<select wire:model="selectedDishId"
|
||||
class="w-full p-2 border rounded bg-gray-700 border-secondary text-gray-100 focus:bg-gray-900 focus:outline-none focus:border-accent-blue">
|
||||
<option value="">Select a dish...</option>
|
||||
@foreach($availableDishes as $dish)
|
||||
<option value="{{ $dish->id }}">{{ $dish->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<div class="mb-6">
|
||||
<p class="text-gray-400 text-sm italic">
|
||||
No dishes available for this user.
|
||||
<a href="{{ route('dishes.index') }}" class="text-accent-blue hover:underline">Add dishes</a> first.
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button wire:click="cancel"
|
||||
class="px-4 py-2 border-2 border-secondary text-gray-100 rounded hover:bg-gray-700 transition-colors duration-200">
|
||||
Cancel
|
||||
</button>
|
||||
@if(count($availableDishes) > 0)
|
||||
<button wire:click="saveDish"
|
||||
class="px-4 py-2 bg-primary text-white rounded hover:bg-secondary transition-colors duration-200">
|
||||
Save
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Add Dish Modal -->
|
||||
@if($showAddDishModal)
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-gray-600 border-2 border-secondary rounded-lg p-6 w-full max-w-md mx-4">
|
||||
<h2 class="text-xl text-accent-blue mb-4">Add Dish</h2>
|
||||
<p class="text-gray-300 text-sm mb-4">
|
||||
Add a dish for {{ \Carbon\Carbon::parse($addDate)->format('M j, Y') }}
|
||||
</p>
|
||||
|
||||
@if(count($addAvailableUsers) > 0)
|
||||
<!-- User selection -->
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium mb-2">User</label>
|
||||
<select wire:model.live="addUserId"
|
||||
class="w-full p-2 border rounded bg-gray-700 border-secondary text-gray-100 focus:bg-gray-900 focus:outline-none focus:border-accent-blue">
|
||||
<option value="">Select a user...</option>
|
||||
@foreach($addAvailableUsers as $user)
|
||||
<option value="{{ $user->id }}">{{ $user->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Dish selection (shows after user is selected) -->
|
||||
@if($addUserId)
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium mb-2">Dish</label>
|
||||
@if(count($addAvailableDishes) > 0)
|
||||
<select wire:model="addSelectedDishId"
|
||||
class="w-full p-2 border rounded bg-gray-700 border-secondary text-gray-100 focus:bg-gray-900 focus:outline-none focus:border-accent-blue">
|
||||
<option value="">Select a dish...</option>
|
||||
@foreach($addAvailableDishes as $dish)
|
||||
<option value="{{ $dish->id }}">{{ $dish->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<p class="text-gray-400 text-sm italic">
|
||||
No dishes available for this user.
|
||||
<a href="{{ route('dishes.index') }}" class="text-accent-blue hover:underline">Add dishes</a> first.
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<div class="mb-6">
|
||||
<p class="text-gray-400 text-sm italic">
|
||||
No users available.
|
||||
<a href="{{ route('users.index') }}" class="text-accent-blue hover:underline">Add users</a> first.
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button wire:click="cancel"
|
||||
class="px-4 py-2 border-2 border-secondary text-gray-100 rounded hover:bg-gray-700 transition-colors duration-200">
|
||||
Cancel
|
||||
</button>
|
||||
@if(count($addAvailableUsers) > 0 && count($addAvailableDishes) > 0)
|
||||
<button wire:click="saveAddDish"
|
||||
class="px-4 py-2 bg-primary text-white rounded hover:bg-secondary transition-colors duration-200">
|
||||
Add
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in a new issue