283 lines
9.3 KiB
PHP
283 lines
9.3 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Appointments;
|
|
|
|
use Livewire\Component;
|
|
use App\Models\Appointment;
|
|
use App\Models\Technician;
|
|
use Carbon\Carbon;
|
|
|
|
class Calendar extends Component
|
|
{
|
|
public $currentDate;
|
|
public $currentMonth;
|
|
public $currentYear;
|
|
public $selectedDate;
|
|
public $selectedTechnician = '';
|
|
public $viewType = 'month'; // month, week, day
|
|
public $calendarDays = [];
|
|
public $appointments = [];
|
|
public $technicians = [];
|
|
public $showAppointmentModal = false;
|
|
public $selectedAppointment = null;
|
|
|
|
public function mount()
|
|
{
|
|
$this->currentDate = now();
|
|
$this->currentMonth = $this->currentDate->month;
|
|
$this->currentYear = $this->currentDate->year;
|
|
$this->selectedDate = $this->currentDate->format('Y-m-d');
|
|
$this->technicians = Technician::where('status', 'active')->orderBy('first_name')->get();
|
|
$this->generateCalendar();
|
|
$this->loadAppointments();
|
|
}
|
|
|
|
public function updatedSelectedTechnician()
|
|
{
|
|
$this->loadAppointments();
|
|
}
|
|
|
|
public function setViewType($type)
|
|
{
|
|
$this->viewType = $type;
|
|
$this->generateCalendar();
|
|
$this->loadAppointments();
|
|
}
|
|
|
|
public function previousPeriod()
|
|
{
|
|
switch ($this->viewType) {
|
|
case 'month':
|
|
$this->currentDate = $this->currentDate->subMonth();
|
|
break;
|
|
case 'week':
|
|
$this->currentDate = $this->currentDate->subWeek();
|
|
break;
|
|
case 'day':
|
|
$this->currentDate = $this->currentDate->subDay();
|
|
break;
|
|
}
|
|
|
|
$this->currentMonth = $this->currentDate->month;
|
|
$this->currentYear = $this->currentDate->year;
|
|
$this->generateCalendar();
|
|
$this->loadAppointments();
|
|
}
|
|
|
|
public function nextPeriod()
|
|
{
|
|
switch ($this->viewType) {
|
|
case 'month':
|
|
$this->currentDate = $this->currentDate->addMonth();
|
|
break;
|
|
case 'week':
|
|
$this->currentDate = $this->currentDate->addWeek();
|
|
break;
|
|
case 'day':
|
|
$this->currentDate = $this->currentDate->addDay();
|
|
break;
|
|
}
|
|
|
|
$this->currentMonth = $this->currentDate->month;
|
|
$this->currentYear = $this->currentDate->year;
|
|
$this->generateCalendar();
|
|
$this->loadAppointments();
|
|
}
|
|
|
|
public function today()
|
|
{
|
|
$this->currentDate = now();
|
|
$this->currentMonth = $this->currentDate->month;
|
|
$this->currentYear = $this->currentDate->year;
|
|
$this->selectedDate = $this->currentDate->format('Y-m-d');
|
|
$this->generateCalendar();
|
|
$this->loadAppointments();
|
|
}
|
|
|
|
public function selectDate($date)
|
|
{
|
|
$this->selectedDate = $date;
|
|
$this->currentDate = Carbon::parse($date);
|
|
$this->dispatch('date-selected', date: $date);
|
|
}
|
|
|
|
public function showAppointmentDetails($appointmentId)
|
|
{
|
|
$this->selectedAppointment = Appointment::with(['customer', 'vehicle', 'assignedTechnician'])
|
|
->find($appointmentId);
|
|
$this->showAppointmentModal = true;
|
|
}
|
|
|
|
public function closeAppointmentModal()
|
|
{
|
|
$this->showAppointmentModal = false;
|
|
$this->selectedAppointment = null;
|
|
}
|
|
|
|
private function generateCalendar()
|
|
{
|
|
$this->calendarDays = [];
|
|
|
|
switch ($this->viewType) {
|
|
case 'month':
|
|
$this->generateMonthCalendar();
|
|
break;
|
|
case 'week':
|
|
$this->generateWeekCalendar();
|
|
break;
|
|
case 'day':
|
|
$this->generateDayCalendar();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private function generateMonthCalendar()
|
|
{
|
|
$startOfMonth = $this->currentDate->copy()->startOfMonth();
|
|
$endOfMonth = $this->currentDate->copy()->endOfMonth();
|
|
$startOfCalendar = $startOfMonth->copy()->startOfWeek();
|
|
$endOfCalendar = $endOfMonth->copy()->endOfWeek();
|
|
|
|
$current = $startOfCalendar->copy();
|
|
$this->calendarDays = [];
|
|
|
|
while ($current <= $endOfCalendar) {
|
|
$this->calendarDays[] = [
|
|
'date' => $current->format('Y-m-d'),
|
|
'day' => $current->day,
|
|
'isCurrentMonth' => $current->month === $this->currentMonth,
|
|
'isToday' => $current->isToday(),
|
|
'isSelected' => $current->format('Y-m-d') === $this->selectedDate,
|
|
'dayName' => $current->format('D'),
|
|
];
|
|
$current->addDay();
|
|
}
|
|
}
|
|
|
|
private function generateWeekCalendar()
|
|
{
|
|
$startOfWeek = $this->currentDate->copy()->startOfWeek();
|
|
$this->calendarDays = [];
|
|
|
|
for ($i = 0; $i < 7; $i++) {
|
|
$date = $startOfWeek->copy()->addDays($i);
|
|
$this->calendarDays[] = [
|
|
'date' => $date->format('Y-m-d'),
|
|
'day' => $date->day,
|
|
'isCurrentMonth' => true,
|
|
'isToday' => $date->isToday(),
|
|
'isSelected' => $date->format('Y-m-d') === $this->selectedDate,
|
|
'dayName' => $date->format('l'),
|
|
'fullDate' => $date->format('M j'),
|
|
];
|
|
}
|
|
}
|
|
|
|
private function generateDayCalendar()
|
|
{
|
|
$this->calendarDays = [[
|
|
'date' => $this->currentDate->format('Y-m-d'),
|
|
'day' => $this->currentDate->day,
|
|
'isCurrentMonth' => true,
|
|
'isToday' => $this->currentDate->isToday(),
|
|
'isSelected' => true,
|
|
'dayName' => $this->currentDate->format('l'),
|
|
'fullDate' => $this->currentDate->format('F j, Y'),
|
|
]];
|
|
}
|
|
|
|
private function loadAppointments()
|
|
{
|
|
$query = Appointment::with(['customer', 'vehicle', 'assignedTechnician']);
|
|
|
|
switch ($this->viewType) {
|
|
case 'month':
|
|
$startDate = $this->currentDate->copy()->startOfMonth();
|
|
$endDate = $this->currentDate->copy()->endOfMonth();
|
|
break;
|
|
case 'week':
|
|
$startDate = $this->currentDate->copy()->startOfWeek();
|
|
$endDate = $this->currentDate->copy()->endOfWeek();
|
|
break;
|
|
case 'day':
|
|
$startDate = $this->currentDate->copy()->startOfDay();
|
|
$endDate = $this->currentDate->copy()->endOfDay();
|
|
break;
|
|
}
|
|
|
|
$query->whereBetween('scheduled_datetime', [$startDate, $endDate]);
|
|
|
|
if ($this->selectedTechnician) {
|
|
$query->where('assigned_technician_id', $this->selectedTechnician);
|
|
}
|
|
|
|
$appointments = $query->orderBy('scheduled_datetime')->get();
|
|
|
|
// Group appointments by date and convert to array for blade template
|
|
$this->appointments = $appointments->groupBy(function ($appointment) {
|
|
return $appointment->scheduled_datetime->format('Y-m-d');
|
|
})->map(function ($dayAppointments) {
|
|
return $dayAppointments->map(function ($appointment) {
|
|
return [
|
|
'id' => $appointment->id,
|
|
'scheduled_datetime' => $appointment->scheduled_datetime->toISOString(),
|
|
'service_requested' => $appointment->service_requested,
|
|
'status' => $appointment->status,
|
|
'status_color' => $appointment->status_color,
|
|
'customer' => [
|
|
'first_name' => $appointment->customer->first_name ?? '',
|
|
'last_name' => $appointment->customer->last_name ?? '',
|
|
],
|
|
'assigned_technician' => [
|
|
'first_name' => $appointment->assignedTechnician->first_name ?? '',
|
|
'last_name' => $appointment->assignedTechnician->last_name ?? '',
|
|
],
|
|
];
|
|
})->toArray();
|
|
})->toArray();
|
|
}
|
|
|
|
public function getTimeSlots()
|
|
{
|
|
$slots = [];
|
|
$start = 8; // 8 AM
|
|
$end = 18; // 6 PM
|
|
|
|
for ($hour = $start; $hour < $end; $hour++) {
|
|
$slots[] = [
|
|
'time' => sprintf('%02d:00', $hour),
|
|
'label' => Carbon::createFromTime($hour, 0)->format('g:i A'),
|
|
];
|
|
$slots[] = [
|
|
'time' => sprintf('%02d:30', $hour),
|
|
'label' => Carbon::createFromTime($hour, 30)->format('g:i A'),
|
|
];
|
|
}
|
|
|
|
return $slots;
|
|
}
|
|
|
|
public function getCurrentPeriodLabel()
|
|
{
|
|
switch ($this->viewType) {
|
|
case 'month':
|
|
return $this->currentDate->format('F Y');
|
|
case 'week':
|
|
$start = $this->currentDate->copy()->startOfWeek();
|
|
$end = $this->currentDate->copy()->endOfWeek();
|
|
return $start->format('M j') . ' - ' . $end->format('M j, Y');
|
|
case 'day':
|
|
return $this->currentDate->format('l, F j, Y');
|
|
}
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.appointments.calendar', [
|
|
'currentPeriodLabel' => $this->getCurrentPeriodLabel(),
|
|
'timeSlots' => $this->getTimeSlots(),
|
|
'appointmentCount' => collect($this->appointments)->flatten(1)->count(),
|
|
]);
|
|
}
|
|
}
|