424 lines
15 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreEmployeeRequest;
use App\Http\Requests\UpdateEmployeeRequest;
use App\Http\Resources\Employee\EmployeeResource;
use App\Http\Resources\Employee\EmployeeCollection;
use App\Http\Resources\Intervention\InterventionResource;
use App\Repositories\EmployeeRepositoryInterface;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;
class EmployeeController extends Controller
{
public function __construct(
private readonly EmployeeRepositoryInterface $employeeRepository
) {
}
/**
* Display a listing of employees (paginated).
*/
public function index(Request $request): JsonResponse
{
try {
$perPage = (int) $request->get('per_page', 15);
$filters = [
'search' => $request->get('search'),
'active' => $request->get('active'),
'sort_by' => $request->get('sort_by', 'last_name'),
'sort_direction' => $request->get('sort_direction', 'asc'),
];
// Remove null filters
$filters = array_filter($filters, function ($value) {
return $value !== null && $value !== '';
});
$result = $this->employeeRepository->getPaginated($perPage, $filters);
return response()->json([
'data' => new EmployeeCollection($result['employees']),
'pagination' => $result['pagination'],
'message' => 'Employés récupérés avec succès.',
], 200);
} catch (\Exception $e) {
Log::error('Error fetching employees: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération des employés.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Display paginated employees.
*/
public function paginated(Request $request): JsonResponse
{
try {
$perPage = (int) $request->get('per_page', 15);
$result = $this->employeeRepository->getPaginated($perPage, []);
return response()->json([
'data' => new EmployeeCollection($result['employees']),
'pagination' => $result['pagination'],
'message' => 'Employés récupérés avec succès.',
], 200);
} catch (\Exception $e) {
Log::error('Error fetching paginated employees: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération des employés.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Get active employees only.
*/
public function active(): EmployeeCollection|JsonResponse
{
try {
$employees = $this->employeeRepository->getActive();
return new EmployeeCollection($employees);
} catch (\Exception $e) {
Log::error('Error fetching active employees: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération des employés actifs.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Get employees with thanatopractitioner data.
*/
public function withThanatopractitioner(): EmployeeCollection|JsonResponse
{
try {
$employees = $this->employeeRepository->getWithThanatopractitioner();
return new EmployeeCollection($employees);
} catch (\Exception $e) {
Log::error('Error fetching employees with thanatopractitioner: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération des employés avec données thanatopractitioner.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Get employee statistics.
*/
public function statistics(): JsonResponse
{
try {
$statistics = $this->employeeRepository->getStatistics();
return response()->json([
'data' => $statistics,
'message' => 'Statistiques des employés récupérées avec succès.',
], 200);
} catch (\Exception $e) {
Log::error('Error fetching employee statistics: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération des statistiques.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Store a newly created employee.
*/
public function store(StoreEmployeeRequest $request): EmployeeResource|JsonResponse
{
try {
$employee = $this->employeeRepository->create($request->validated());
return new EmployeeResource($employee);
} catch (\Exception $e) {
Log::error('Error creating employee: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
'data' => $request->validated(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la création de l\'employé.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Display the specified employee.
*/
public function show(string $id): EmployeeResource|JsonResponse
{
try {
$employee = $this->employeeRepository->find($id);
if (!$employee) {
return response()->json([
'message' => 'Employé non trouvé.',
], 404);
}
return new EmployeeResource($employee);
} catch (\Exception $e) {
Log::error('Error fetching employee: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
'employee_id' => $id,
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération de l\'employé.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Get the intervention agenda for a specific employee.
*/
public function agenda(Request $request, string $id): JsonResponse
{
$validator = Validator::make($request->all(), [
'month' => ['nullable', 'date_format:Y-m'],
'start_date' => ['nullable', 'date'],
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
'status' => ['nullable', 'string'],
'type' => ['nullable', 'string'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Paramètres d\'agenda invalides.',
'errors' => $validator->errors(),
], 422);
}
try {
$employee = $this->employeeRepository->find($id);
if (!$employee) {
return response()->json([
'message' => 'Employé non trouvé.',
], 404);
}
$employee->load('thanatopractitioner');
if (!$employee->thanatopractitioner) {
return response()->json([
'message' => 'Aucun agenda disponible pour cet employé.',
'employee' => [
'id' => $employee->id,
'full_name' => $employee->full_name,
'job_title' => $employee->job_title,
],
'filters' => [
'month' => $request->input('month'),
'start_date' => $request->input('start_date'),
'end_date' => $request->input('end_date'),
'status' => $request->input('status'),
'type' => $request->input('type'),
],
'data' => [],
'meta' => [
'total' => 0,
'status_summary' => [],
],
], 200);
}
[$startDate, $endDate] = $this->resolveAgendaPeriod(
$request->input('month'),
$request->input('start_date'),
$request->input('end_date')
);
$interventions = $employee->thanatopractitioner
->interventions()
->with([
'client',
'deceased',
'location',
'quote',
'practitioners.employee',
])
->when($startDate, function ($query) use ($startDate) {
$query->where('scheduled_at', '>=', $startDate);
})
->when($endDate, function ($query) use ($endDate) {
$query->where('scheduled_at', '<=', $endDate);
})
->when($request->filled('status'), function ($query) use ($request) {
$query->where('status', $request->string('status'));
})
->when($request->filled('type'), function ($query) use ($request) {
$query->where('type', $request->string('type'));
})
->orderBy('scheduled_at')
->get();
return response()->json([
'message' => 'Agenda employé récupéré avec succès.',
'employee' => [
'id' => $employee->id,
'full_name' => $employee->full_name,
'job_title' => $employee->job_title,
'thanatopractitioner_id' => $employee->thanatopractitioner->id,
],
'filters' => [
'month' => $request->input('month'),
'start_date' => $startDate?->toDateTimeString(),
'end_date' => $endDate?->toDateTimeString(),
'status' => $request->input('status'),
'type' => $request->input('type'),
],
'data' => InterventionResource::collection($interventions)->resolve(),
'meta' => [
'total' => $interventions->count(),
'status_summary' => $interventions
->groupBy('status')
->map(fn ($group) => $group->count())
->toArray(),
],
], 200);
} catch (\Exception $e) {
Log::error('Error fetching employee agenda: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
'employee_id' => $id,
'filters' => $request->all(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la récupération de l\'agenda employé.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Update the specified employee.
*/
public function update(UpdateEmployeeRequest $request, string $id): EmployeeResource|JsonResponse
{
try {
$updated = $this->employeeRepository->update($id, $request->validated());
if (!$updated) {
return response()->json([
'message' => 'Employé non trouvé ou échec de la mise à jour.',
], 404);
}
$employee = $this->employeeRepository->find($id);
return new EmployeeResource($employee);
} catch (\Exception $e) {
Log::error('Error updating employee: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
'employee_id' => $id,
'data' => $request->validated(),
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la mise à jour de l\'employé.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Remove the specified employee.
*/
public function destroy(string $id): JsonResponse
{
try {
$deleted = $this->employeeRepository->delete($id);
if (!$deleted) {
return response()->json([
'message' => 'Employé non trouvé ou échec de la suppression.',
], 404);
}
return response()->json([
'message' => 'Employé supprimé avec succès.',
], 200);
} catch (\Exception $e) {
Log::error('Error deleting employee: ' . $e->getMessage(), [
'exception' => $e,
'trace' => $e->getTraceAsString(),
'employee_id' => $id,
]);
return response()->json([
'message' => 'Une erreur est survenue lors de la suppression de l\'employé.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* Resolve the requested agenda period.
*
* Defaults to the current month when no explicit range is provided.
*
* @return array{0: \Carbon\Carbon, 1: \Carbon\Carbon}
*/
private function resolveAgendaPeriod(?string $month, ?string $startDate, ?string $endDate): array
{
if ($month) {
$reference = Carbon::createFromFormat('Y-m', $month)->startOfMonth();
return [$reference->copy()->startOfMonth(), $reference->copy()->endOfMonth()];
}
if ($startDate || $endDate) {
return [
$startDate ? Carbon::parse($startDate)->startOfDay() : Carbon::now()->startOfMonth(),
$endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now()->endOfMonth(),
];
}
return [Carbon::now()->startOfMonth(), Carbon::now()->endOfMonth()];
}
}