Initial Ladill Queue release — enterprise QMS standalone app.
Deploy Ladill Queue / deploy (push) Successful in 56s
Deploy Ladill Queue / deploy (push) Successful in 56s
Phases 1–6: tickets, counters, displays, appointments, workflows, rules, analytics, reports, feedback, admin, device API, and Gitea deploy workflow for queue.ladill.com. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Qms;
|
||||
|
||||
use App\Models\CustomerFeedback;
|
||||
use App\Models\QueueAppointment;
|
||||
use App\Models\ServiceQueue;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class AnalyticsService
|
||||
{
|
||||
public function __construct(
|
||||
protected DashboardStats $dashboard,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function overview(string $ownerRef, int $organizationId, ?int $branchId = null): array
|
||||
{
|
||||
$stats = $this->dashboard->forOrganization($ownerRef, $organizationId, $branchId);
|
||||
|
||||
$today = Ticket::owned($ownerRef)
|
||||
->where('organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
|
||||
->whereDate('issued_at', today());
|
||||
|
||||
$bySource = (clone $today)
|
||||
->selectRaw('source, COUNT(*) as total')
|
||||
->groupBy('source')
|
||||
->pluck('total', 'source')
|
||||
->all();
|
||||
|
||||
$byPriority = (clone $today)
|
||||
->selectRaw('priority, COUNT(*) as total')
|
||||
->groupBy('priority')
|
||||
->pluck('total', 'priority')
|
||||
->all();
|
||||
|
||||
$appointmentsToday = QueueAppointment::owned($ownerRef)
|
||||
->where('organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
|
||||
->whereDate('scheduled_at', today())
|
||||
->count();
|
||||
|
||||
$feedbackAvg = CustomerFeedback::owned($ownerRef)
|
||||
->where('organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->whereHas('ticket', fn ($t) => $t->where('branch_id', $branchId)))
|
||||
->where('created_at', '>=', now()->subDays(30))
|
||||
->avg('rating');
|
||||
|
||||
$queueLoad = ServiceQueue::owned($ownerRef)
|
||||
->where('organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
|
||||
->where('is_active', true)
|
||||
->withCount(['tickets as waiting_count' => fn ($q) => $q->where('status', 'waiting')])
|
||||
->orderByDesc('waiting_count')
|
||||
->limit(5)
|
||||
->get()
|
||||
->map(fn ($q) => ['name' => $q->name, 'waiting' => $q->waiting_count])
|
||||
->all();
|
||||
|
||||
return array_merge($stats, [
|
||||
'tickets_by_source' => $bySource,
|
||||
'tickets_by_priority' => $byPriority,
|
||||
'appointments_today' => $appointmentsToday,
|
||||
'feedback_avg_30d' => round((float) $feedbackAvg, 1),
|
||||
'busiest_queues' => $queueLoad,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{date: string, issued: int, completed: int}>
|
||||
*/
|
||||
public function dailyTrend(string $ownerRef, int $organizationId, int $days = 14, ?int $branchId = null): array
|
||||
{
|
||||
$from = now()->subDays($days - 1)->startOfDay();
|
||||
|
||||
$issued = Ticket::owned($ownerRef)
|
||||
->where('organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
|
||||
->where('issued_at', '>=', $from)
|
||||
->selectRaw('DATE(issued_at) as d, COUNT(*) as c')
|
||||
->groupBy('d')
|
||||
->pluck('c', 'd');
|
||||
|
||||
$completed = Ticket::owned($ownerRef)
|
||||
->where('organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
|
||||
->where('status', 'completed')
|
||||
->where('completed_at', '>=', $from)
|
||||
->selectRaw('DATE(completed_at) as d, COUNT(*) as c')
|
||||
->groupBy('d')
|
||||
->pluck('c', 'd');
|
||||
|
||||
$trend = [];
|
||||
for ($i = 0; $i < $days; $i++) {
|
||||
$date = $from->copy()->addDays($i)->toDateString();
|
||||
$trend[] = [
|
||||
'date' => $date,
|
||||
'issued' => (int) ($issued[$date] ?? 0),
|
||||
'completed' => (int) ($completed[$date] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
return $trend;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user