Files
Kol-Manager/app/Services/DashboardService.php
2026-04-01 08:04:56 +07:00

138 lines
4.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Client;
use App\Models\ContentBrief;
use App\Models\Event;
use App\Models\Invoice;
use App\Models\SocialStat;
use Illuminate\Support\Collection;
final class DashboardService
{
public function getStats(): array
{
return [
'total_clients' => Client::count(),
'active_clients' => Client::where('status', 'active')->count(),
'upcoming_events' => Event::upcoming(24 * 90)->count(), // 3 months
'pending_invoices' => Invoice::where('status', 'sent')->count(),
'total_revenue' => Invoice::where('status', 'paid')->sum('total'),
'content_in_progress' => ContentBrief::whereNotIn('status', ['posted', 'archived'])->count(),
];
}
/**
* Combined today's activities: events (today or ongoing), invoices due, content targets.
*/
public function getTodayActivities(): Collection
{
$activities = collect();
// Events starting today OR currently ongoing (started before today, ends today or later)
$events = Event::with('client')
->where(function ($q) {
$q->whereDate('start_time', today())
->orWhere(function ($q2) {
$q2->whereDate('start_time', '<', today())
->where(function ($q3) {
$q3->whereDate('end_time', '>=', today())
->orWhereNull('end_time');
})
->whereNotIn('status', ['completed', 'cancelled']);
});
})
->orderBy('start_time')
->limit(15)
->get();
foreach ($events as $e) {
$timeLabel = $e->is_all_day
? 'All day'
: ($e->start_time->format('H:i') . ($e->end_time ? ' – ' . $e->end_time->format('H:i') : ''));
$activities->push([
'type' => 'event',
'icon' => '📅',
'title' => $e->title,
'subtitle' => $timeLabel . ($e->client ? ' · ' . $e->client->name : ''),
'label' => $e->event_type,
'badge' => $e->status->label(),
'badge_class' => $e->status->badgeClass(),
'color' => $e->color ?? '#FFB7C5',
'priority' => $e->is_priority,
'sort_key' => $e->start_time->timestamp,
]);
}
// Invoices due today (not yet paid or cancelled)
$invoices = Invoice::with('client')
->whereDate('due_date', today())
->whereNotIn('status', ['paid', 'cancelled'])
->get();
foreach ($invoices as $inv) {
$activities->push([
'type' => 'invoice',
'icon' => '🧾',
'title' => $inv->title ?? $inv->invoice_number,
'subtitle' => ($inv->client?->name ?? 'No client') . ' · ' . ($inv->formatted_total ?? ''),
'label' => 'Invoice Due',
'badge' => $inv->status->label(),
'badge_class' => $inv->status->badgeClass(),
'color' => '#FDFD96',
'priority' => false,
'sort_key' => today()->setTime(23, 59, 0)->timestamp,
]);
}
// Content briefs with target date today (not posted/archived)
$content = ContentBrief::with('client')
->whereDate('target_date', today())
->whereNotIn('status', ['posted', 'archived'])
->get();
foreach ($content as $c) {
$activities->push([
'type' => 'content',
'icon' => $c->platform_icon,
'title' => $c->title,
'subtitle' => ucfirst($c->platform) . ($c->client ? ' · ' . $c->client->name : ''),
'label' => 'Content Target',
'badge' => $c->status->label(),
'badge_class' => 'badge-ghost',
'color' => '#E8D5F5',
'priority' => false,
'sort_key' => today()->setTime(12, 0, 0)->timestamp,
]);
}
return $activities->sortBy('sort_key')->values();
}
public function getAlertCount(): int
{
return Event::upcoming(24)->count();
}
public function getLatestInvoices(): Collection
{
return Invoice::with('client')
->latest()
->limit(5)
->get();
}
public function getSocialStats(): Collection
{
return SocialStat::whereIn('id', function ($query): void {
$query->selectRaw('MAX(id)')
->from('social_stats')
->groupBy('platform');
})->get();
}
}