257 lines
8.5 KiB
PHP
257 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\TechnicianManagement;
|
|
|
|
use Livewire\Component;
|
|
use App\Models\Technician;
|
|
use App\Models\TechnicianWorkload;
|
|
use Livewire\Attributes\On;
|
|
use Carbon\Carbon;
|
|
|
|
class WorkloadManagement extends Component
|
|
{
|
|
public $showModal = false;
|
|
public $technicianId = null;
|
|
public $technician = null;
|
|
public $workloadId = null;
|
|
public $editing = false;
|
|
|
|
// Date filters
|
|
public $startDate = '';
|
|
public $endDate = '';
|
|
public $viewMode = 'week'; // week, month, custom
|
|
|
|
// Form fields
|
|
public $workload_date = '';
|
|
public $scheduled_hours = 8.0;
|
|
public $actual_hours = 0.0;
|
|
public $overtime_hours = 0.0;
|
|
public $jobs_assigned = 0;
|
|
public $jobs_completed = 0;
|
|
public $utilization_rate = 0.0;
|
|
public $efficiency_rate = 0.0;
|
|
public $notes = '';
|
|
|
|
protected $rules = [
|
|
'workload_date' => 'required|date|unique:technician_workloads,workload_date,NULL,id,technician_id,' . null,
|
|
'scheduled_hours' => 'required|numeric|min:0|max:24',
|
|
'actual_hours' => 'required|numeric|min:0|max:24',
|
|
'overtime_hours' => 'nullable|numeric|min:0|max:12',
|
|
'jobs_assigned' => 'required|integer|min:0',
|
|
'jobs_completed' => 'required|integer|min:0',
|
|
'notes' => 'nullable|string|max:1000'
|
|
];
|
|
|
|
public function mount()
|
|
{
|
|
$this->setWeekView();
|
|
$this->workload_date = now()->format('Y-m-d');
|
|
}
|
|
|
|
#[On('manage-workload')]
|
|
public function manageWorkload($technicianId)
|
|
{
|
|
$this->technicianId = $technicianId;
|
|
$this->technician = Technician::with('workloads')->findOrFail($technicianId);
|
|
$this->showModal = true;
|
|
$this->resetForm();
|
|
}
|
|
|
|
public function updatedViewMode()
|
|
{
|
|
switch ($this->viewMode) {
|
|
case 'week':
|
|
$this->setWeekView();
|
|
break;
|
|
case 'month':
|
|
$this->setMonthView();
|
|
break;
|
|
// custom stays as is
|
|
}
|
|
}
|
|
|
|
public function setWeekView()
|
|
{
|
|
$this->startDate = now()->startOfWeek()->format('Y-m-d');
|
|
$this->endDate = now()->endOfWeek()->format('Y-m-d');
|
|
}
|
|
|
|
public function setMonthView()
|
|
{
|
|
$this->startDate = now()->startOfMonth()->format('Y-m-d');
|
|
$this->endDate = now()->endOfMonth()->format('Y-m-d');
|
|
}
|
|
|
|
public function previousPeriod()
|
|
{
|
|
if ($this->viewMode === 'week') {
|
|
$start = Carbon::parse($this->startDate)->subWeek();
|
|
$this->startDate = $start->format('Y-m-d');
|
|
$this->endDate = $start->endOfWeek()->format('Y-m-d');
|
|
} elseif ($this->viewMode === 'month') {
|
|
$start = Carbon::parse($this->startDate)->subMonth()->startOfMonth();
|
|
$this->startDate = $start->format('Y-m-d');
|
|
$this->endDate = $start->endOfMonth()->format('Y-m-d');
|
|
}
|
|
}
|
|
|
|
public function nextPeriod()
|
|
{
|
|
if ($this->viewMode === 'week') {
|
|
$start = Carbon::parse($this->startDate)->addWeek();
|
|
$this->startDate = $start->format('Y-m-d');
|
|
$this->endDate = $start->endOfWeek()->format('Y-m-d');
|
|
} elseif ($this->viewMode === 'month') {
|
|
$start = Carbon::parse($this->startDate)->addMonth()->startOfMonth();
|
|
$this->startDate = $start->format('Y-m-d');
|
|
$this->endDate = $start->endOfMonth()->format('Y-m-d');
|
|
}
|
|
}
|
|
|
|
public function addWorkloadRecord()
|
|
{
|
|
$this->resetForm();
|
|
$this->editing = false;
|
|
}
|
|
|
|
public function editWorkload($workloadId)
|
|
{
|
|
$workload = TechnicianWorkload::findOrFail($workloadId);
|
|
|
|
$this->workloadId = $workload->id;
|
|
$this->workload_date = $workload->workload_date->format('Y-m-d');
|
|
$this->scheduled_hours = $workload->scheduled_hours;
|
|
$this->actual_hours = $workload->actual_hours;
|
|
$this->overtime_hours = $workload->overtime_hours;
|
|
$this->jobs_assigned = $workload->jobs_assigned;
|
|
$this->jobs_completed = $workload->jobs_completed;
|
|
$this->notes = $workload->notes;
|
|
|
|
$this->editing = true;
|
|
}
|
|
|
|
public function saveWorkload()
|
|
{
|
|
// Update unique rule for editing
|
|
if ($this->editing) {
|
|
$this->rules['workload_date'] = 'required|date|unique:technician_workloads,workload_date,' . $this->workloadId . ',id,technician_id,' . $this->technicianId;
|
|
} else {
|
|
$this->rules['workload_date'] = 'required|date|unique:technician_workloads,workload_date,NULL,id,technician_id,' . $this->technicianId;
|
|
}
|
|
|
|
$this->validate();
|
|
|
|
$data = [
|
|
'technician_id' => $this->technicianId,
|
|
'workload_date' => $this->workload_date,
|
|
'scheduled_hours' => $this->scheduled_hours,
|
|
'actual_hours' => $this->actual_hours,
|
|
'overtime_hours' => $this->overtime_hours ?? 0,
|
|
'jobs_assigned' => $this->jobs_assigned,
|
|
'jobs_completed' => $this->jobs_completed,
|
|
'notes' => $this->notes,
|
|
];
|
|
|
|
if ($this->editing) {
|
|
$workload = TechnicianWorkload::findOrFail($this->workloadId);
|
|
$workload->update($data);
|
|
|
|
// Recalculate rates
|
|
$workload->utilization_rate = $workload->calculateUtilizationRate();
|
|
$workload->efficiency_rate = $workload->calculateEfficiencyRate();
|
|
$workload->save();
|
|
|
|
session()->flash('message', 'Workload record updated successfully!');
|
|
} else {
|
|
$workload = TechnicianWorkload::create($data);
|
|
|
|
// Calculate rates
|
|
$workload->utilization_rate = $workload->calculateUtilizationRate();
|
|
$workload->efficiency_rate = $workload->calculateEfficiencyRate();
|
|
$workload->save();
|
|
|
|
session()->flash('message', 'Workload record added successfully!');
|
|
}
|
|
|
|
$this->technician->refresh();
|
|
$this->resetForm();
|
|
}
|
|
|
|
public function deleteWorkload($workloadId)
|
|
{
|
|
TechnicianWorkload::findOrFail($workloadId)->delete();
|
|
$this->technician->refresh();
|
|
session()->flash('message', 'Workload record deleted successfully!');
|
|
}
|
|
|
|
public function closeModal()
|
|
{
|
|
$this->showModal = false;
|
|
$this->resetForm();
|
|
}
|
|
|
|
public function resetForm()
|
|
{
|
|
$this->workloadId = null;
|
|
$this->workload_date = now()->format('Y-m-d');
|
|
$this->scheduled_hours = 8.0;
|
|
$this->actual_hours = 0.0;
|
|
$this->overtime_hours = 0.0;
|
|
$this->jobs_assigned = 0;
|
|
$this->jobs_completed = 0;
|
|
$this->notes = '';
|
|
$this->editing = false;
|
|
$this->resetErrorBag();
|
|
}
|
|
|
|
public function getFilteredWorkloadsProperty()
|
|
{
|
|
if (!$this->technician) return collect();
|
|
|
|
return $this->technician->workloads()
|
|
->whereBetween('workload_date', [$this->startDate, $this->endDate])
|
|
->orderBy('workload_date')
|
|
->get();
|
|
}
|
|
|
|
public function getWorkloadStatsProperty()
|
|
{
|
|
$workloads = $this->filteredWorkloads;
|
|
|
|
if ($workloads->isEmpty()) {
|
|
return [
|
|
'total_scheduled' => 0,
|
|
'total_actual' => 0,
|
|
'total_overtime' => 0,
|
|
'avg_utilization' => 0,
|
|
'avg_efficiency' => 0,
|
|
'total_jobs_assigned' => 0,
|
|
'total_jobs_completed' => 0,
|
|
'completion_rate' => 0
|
|
];
|
|
}
|
|
|
|
$totalJobsAssigned = $workloads->sum('jobs_assigned');
|
|
$totalJobsCompleted = $workloads->sum('jobs_completed');
|
|
|
|
return [
|
|
'total_scheduled' => $workloads->sum('scheduled_hours'),
|
|
'total_actual' => $workloads->sum('actual_hours'),
|
|
'total_overtime' => $workloads->sum('overtime_hours'),
|
|
'avg_utilization' => round($workloads->avg('utilization_rate'), 1),
|
|
'avg_efficiency' => round($workloads->avg('efficiency_rate'), 1),
|
|
'total_jobs_assigned' => $totalJobsAssigned,
|
|
'total_jobs_completed' => $totalJobsCompleted,
|
|
'completion_rate' => $totalJobsAssigned > 0 ? round(($totalJobsCompleted / $totalJobsAssigned) * 100, 1) : 0
|
|
];
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.technician-management.workload-management', [
|
|
'filteredWorkloads' => $this->filteredWorkloads,
|
|
'workloadStats' => $this->workloadStats
|
|
]);
|
|
}
|
|
}
|