src/Controller/Backend/DashboardController.php line 50

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controller\Backend;
  4. use App\Contracts\Platform\Platform;
  5. use App\Entity\Activity;
  6. use App\Entity\Administrator;
  7. use App\Entity\Badge;
  8. use App\Entity\Campaign;
  9. use App\Entity\Complaint;
  10. use App\Entity\ComplaintReason;
  11. use App\Entity\Country;
  12. use App\Entity\Cpm;
  13. use App\Entity\Creator;
  14. use App\Entity\Manager;
  15. use App\Entity\PaymentHistory;
  16. use App\Entity\PaymentProcess;
  17. use App\Entity\ScrapingCreatorsData;
  18. use App\Entity\ScrapingVideosData;
  19. use App\Entity\Settings;
  20. use App\Entity\Speed;
  21. use App\Entity\Sponsor;
  22. use App\Entity\User;
  23. use App\Services\Statistic\GlobalStatisticService;
  24. use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
  25. use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
  26. use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
  27. use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
  28. use EasyCorp\Bundle\EasyAdminBundle\Config\UserMenu;
  29. use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
  30. use Symfony\Component\HttpFoundation\Request;
  31. use Symfony\Component\HttpFoundation\Response;
  32. use Symfony\Component\HttpFoundation\JsonResponse;
  33. use Symfony\Component\Routing\Annotation\Route;
  34. use Symfony\Component\Security\Core\User\UserInterface;
  35. class DashboardController extends AbstractDashboardController
  36. {
  37.     public function __construct(
  38.         private readonly GlobalStatisticService $statistics,
  39.     ) {
  40.     }
  41.     #[Route(
  42.         path'/',
  43.         name'backend-dashboard',
  44.     )]
  45.     public function index(): Response
  46.     {
  47.         // Shell only: the overview is fetched async (backend-dashboard-overview), so
  48.         // landing here — straight after login — never blocks on the stats aggregates.
  49.         return $this->render('backend/dashboard.html.twig');
  50.     }
  51.     #[Route(
  52.         path'/dashboard/overview',
  53.         name'backend-dashboard-overview',
  54.     )]
  55.     public function overviewFragment(): Response
  56.     {
  57.         // "Today" vs the same window yesterday (trend), plus all-time totals + QC backlog.
  58.         $now = new \DateTimeImmutable();
  59.         $today $now->setTime(00);
  60.         $overview $this->statistics->dashboardOverview(
  61.             $today,
  62.             $now,
  63.             $today->modify('-1 day'),
  64.             $now->modify('-1 day'),
  65.             'today',
  66.         );
  67.         return $this->render('backend/_dashboard_overview.html.twig', ['overview' => $overview]);
  68.     }
  69.     #[Route(
  70.         path'/statistic',
  71.         name'statistic-page',
  72.     )]
  73.     public function statistic(Request $request): Response
  74.     {
  75.         [$from$to$period$prevFrom$prevTo] = $this->resolvePeriod($request);
  76.         return $this->render('backend/statistic/statistic.html.twig', [
  77.             'overview' => $this->statistics->overview($from$to$prevFrom$prevTo$period === 'custom' null $period),
  78.             'period' => $period,
  79.             'periods' => [714286090120365],
  80.             'from' => $from?->format('Y-m-d'),
  81.             'to' => $to?->format('Y-m-d'),
  82.         ]);
  83.     }
  84.     #[Route(path'/statistic/data'name'statistic-data')]
  85.     public function statisticData(Request $request): Response
  86.     {
  87.         [$from$to$period$prevFrom$prevTo] = $this->resolvePeriod($request);
  88.         return new JsonResponse([
  89.             'period' => $period,
  90.             'from' => $from?->format('Y-m-d'),
  91.             'to' => $to?->format('Y-m-d'),
  92.             'overview' => $this->statistics->overview($from$to$prevFrom$prevTo$period === 'custom' null $period),
  93.         ]);
  94.     }
  95.     /** @return array{0: ?\DateTimeImmutable, 1: ?\DateTimeImmutable, 2: string} */
  96.     private function resolvePeriod(Request $request): array
  97.     {
  98.         $period = (string) $request->query->get('period''all');
  99.         $now = new \DateTimeImmutable();
  100.         $from null;
  101.         $to null;
  102.         if ($period === 'custom') {
  103.             $f $request->query->get('from');
  104.             $t $request->query->get('to');
  105.             $from $f ? new \DateTimeImmutable($f ' 00:00:00') : null;
  106.             $to $t ? new \DateTimeImmutable($t ' 23:59:59') : null;
  107.         } elseif (ctype_digit($period)) {
  108.             $from $now->modify('-' $period ' days');
  109.             $to $now;
  110.         } // 'all' => open-ended
  111.         // Previous equal-length window immediately before [from, to], for trend.
  112.         $prevFrom null;
  113.         $prevTo null;
  114.         if ($from !== null && $to !== null) {
  115.             $seconds $to->getTimestamp() - $from->getTimestamp();
  116.             $prevFrom $from->modify('-' $seconds ' seconds');
  117.             $prevTo $from;
  118.         }
  119.         return [$from$to$period$prevFrom$prevTo];
  120.     }
  121.     #[Route(
  122.         path'/statistic-global',
  123.         name'statistic-global',
  124.     )]
  125.     public function globalStats(
  126.         GlobalStatisticService $globalStatisticService,
  127.     ): Response {
  128.         // Served from cache (the aggregates take ~20s; the scheduler keeps it
  129.         // warm via app:statistic:global:warm). Shape matches back_office_stats.js.
  130.         return new JsonResponse($globalStatisticService->getCached());
  131.     }
  132.     public function configureUserMenu(UserInterface $user): UserMenu
  133.     {
  134.         /** @var User $user */
  135.         return parent::configureUserMenu($user)
  136.             ->setName($user->getFullname());
  137.     }
  138.     public function configureDashboard(): Dashboard
  139.     {
  140.         return Dashboard::new()
  141.             ->setTitle('<span style="font-weight:800;font-size:1.15rem;letter-spacing:-.02em;">Sound<span style="color:#635bff;">.ME</span></span>')
  142.             ->setTranslationDomain('messages')
  143.             ->renderContentMaximized();
  144.     }
  145.     public function configureAssets(): Assets
  146.     {
  147.         // Global list/datagrid styling for all CRUD tabs. Injected as a raw <link>
  148.         // (not addCssFile) to bypass the asset() manifest pipeline (no webpack build locally).
  149.         return parent::configureAssets()
  150.             ->addHtmlContentToHead('<link rel="stylesheet" href="/assets/css/sm-admin.css">');
  151.     }
  152.     public function configureMenuItems(): iterable
  153.     {
  154.         return [
  155.             MenuItem::linkToDashboard('Dashboard''fa fa-home'),
  156.             MenuItem::linkToRoute('Statistics''fas fa-chart-bar''statistic-page'),
  157.             MenuItem::section('Campaigns'),
  158.             MenuItem::linkToCrud('Campaigns''fa fa-file-invoice-dollar'Campaign::class),
  159.             MenuItem::linkToCrud('Activities''fa fa-chart-line'Activity::class),
  160.             MenuItem::subMenu('Complaints''fa fa-commenting')->setSubItems([
  161.                 MenuItem::linkToCrud('Complaints''fa fa-commenting'Complaint::class),
  162.                 MenuItem::linkToCrud('Complaint Reasons''fas fa-info-circle'ComplaintReason::class),
  163.             ]),
  164.             MenuItem::section('Creators'),
  165.             MenuItem::linkToCrud('All Creators''fas fa-users'Creator::class)
  166.                 ->setController(CreatorCrudController::class),
  167.             MenuItem::linkToCrud('VIP Creators''fas fa-star'Creator::class)
  168.                 ->setController(VipCreatorCrudController::class),
  169.             MenuItem::linkToRoute('Communication''fas fa-comments''backend-communication-creator'),
  170.             MenuItem::subMenu('Scraping''fa fa-file')->setSubItems([
  171.                 MenuItem::linkToRoute('Scrape Creators''fa fa-file''backend-scrape-page'),
  172.                 MenuItem::linkToCrud('Scraped Creators''fa fa-list'Creator::class)
  173.                     ->setController(CreatorScrapingCrudController::class),
  174.                 MenuItem::linkToCrud('Creators Data''fa fa-list'ScrapingCreatorsData::class),
  175.                 MenuItem::linkToCrud('Videos Data''fa fa-video'ScrapingVideosData::class),
  176.             ]),
  177.             MenuItem::section('Sponsors & Billing'),
  178.             MenuItem::linkToCrud('Sponsors''fas fa-dollar-sign'Sponsor::class),
  179.             MenuItem::linkToRoute('Communication''fas fa-comments''backend-communication-sponsor'),
  180.             MenuItem::linkToCrud('Payments''fa fa-money-check-alt'PaymentHistory::class),
  181.             MenuItem::linkToCrud('Payment Process''fa fa-list-check'PaymentProcess::class),
  182.             MenuItem::section('Team'),
  183.             MenuItem::linkToCrud('Managers''fas fa-people-roof'Manager::class),
  184.             MenuItem::linkToCrud('Administrators''fas fa-user-shield'Administrator::class),
  185.             MenuItem::section('System'),
  186.             MenuItem::linkToCrud('Badges''fas fa-certificate'Badge::class),
  187.             MenuItem::linkToCrud('Countries''fas fa-globe-americas'Country::class),
  188.             MenuItem::linkToCrud('CPM''fa fa-jpy'Cpm::class),
  189.             MenuItem::linkToCrud('Speed''fas fa-tachometer-alt'Speed::class),
  190.             MenuItem::linkToCrud('Settings''fas fa-tools'Settings::class)
  191.                 ->setAction(Action::NEW),
  192.         ];
  193.     }
  194. }