libra/app/Services/AnalyticsService.php

125 lines
3.8 KiB
PHP

<?php
namespace App\Services;
use App\Enums\ConsultationStatus;
use App\Enums\UserType;
use App\Models\Consultation;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;
class AnalyticsService
{
/**
* Get translated month labels for chart X-axis.
*
* @return array<string>
*/
public function getMonthLabels(Carbon $startDate, int $months): array
{
return collect(range(0, $months - 1))
->map(fn ($i) => $startDate->copy()->addMonths($i)->translatedFormat('M Y'))
->toArray();
}
/**
* Get monthly new client counts for chart data.
*
* @return array<int>
*/
public function getMonthlyNewClients(Carbon $startDate, int $months): array
{
$endDate = $startDate->copy()->addMonths($months)->endOfMonth();
$data = User::query()
->whereIn('user_type', [UserType::Individual, UserType::Company])
->whereBetween('created_at', [$startDate, $endDate])
->get()
->groupBy(fn ($user) => $user->created_at->format('Y-m'))
->map(fn ($group) => $group->count());
return $this->fillMonthlyData($startDate, $months, $data);
}
/**
* Get monthly consultation counts for chart data.
*
* @return array<int>
*/
public function getMonthlyConsultations(Carbon $startDate, int $months): array
{
$endDate = $startDate->copy()->addMonths($months)->endOfMonth();
$data = Consultation::query()
->whereBetween('booking_date', [$startDate, $endDate])
->get()
->groupBy(fn ($consultation) => $consultation->booking_date->format('Y-m'))
->map(fn ($group) => $group->count());
return $this->fillMonthlyData($startDate, $months, $data);
}
/**
* Get consultation type breakdown (free vs paid).
*
* @return array{free: int, paid: int}
*/
public function getConsultationTypeBreakdown(Carbon $startDate, int $months): array
{
$endDate = $startDate->copy()->addMonths($months)->endOfMonth();
return [
'free' => Consultation::query()
->whereBetween('booking_date', [$startDate, $endDate])
->where('consultation_type', 'free')
->count(),
'paid' => Consultation::query()
->whereBetween('booking_date', [$startDate, $endDate])
->where('consultation_type', 'paid')
->count(),
];
}
/**
* Get monthly no-show rates as percentages.
*
* @return array<float>
*/
public function getMonthlyNoShowRates(Carbon $startDate, int $months): array
{
$results = [];
for ($i = 0; $i < $months; $i++) {
$monthStart = $startDate->copy()->addMonths($i)->startOfMonth();
$monthEnd = $monthStart->copy()->endOfMonth();
$total = Consultation::query()
->whereBetween('booking_date', [$monthStart, $monthEnd])
->whereIn('status', [ConsultationStatus::Completed, ConsultationStatus::NoShow])
->count();
$noShows = Consultation::query()
->whereBetween('booking_date', [$monthStart, $monthEnd])
->where('status', ConsultationStatus::NoShow)
->count();
$results[] = $total > 0 ? round(($noShows / $total) * 100, 1) : 0;
}
return $results;
}
/**
* Fill monthly data array ensuring all months have values.
*
* @return array<int>
*/
private function fillMonthlyData(Carbon $startDate, int $months, Collection $data): array
{
return collect(range(0, $months - 1))
->map(fn ($i) => $data->get($startDate->copy()->addMonths($i)->format('Y-m'), 0))
->toArray();
}
}