90 lines
3.1 KiB
PHP
90 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Dashboard\Widget;
|
|
|
|
use App\Dashboard\Contract\DashboardWidgetProviderInterface;
|
|
use App\Entity\BookingEditDraft;
|
|
use App\Model\DashboardWidget;
|
|
use App\Model\DashboardWidgetEntry;
|
|
use App\Repository\BookingEditDraftRepository;
|
|
use App\Security\Role;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
|
|
/**
|
|
* Customers whose changes cannot be saved.
|
|
*
|
|
* A draft is deleted as soon as an update succeeds, so one that has been around for days belongs to
|
|
* somebody BusPro keeps refusing — and nothing else tells anyone. One booking accumulated
|
|
* thirty-seven failed attempts across two months before it was noticed by accident; the customer
|
|
* had entered seventy-nine participants, none of which ever reached BusPro. The point of this card
|
|
* is that the next one gets noticed in the first week instead.
|
|
*
|
|
* Each entry links to the draft, since the age alone does not say what is going wrong.
|
|
*/
|
|
class StuckBookingDraftsWidgetProvider implements DashboardWidgetProviderInterface
|
|
{
|
|
/**
|
|
* How long a draft must have survived to be worth reporting.
|
|
*
|
|
* Short enough to catch a customer inside their first week of trying, long enough that an edit
|
|
* somebody merely abandoned over a weekend does not fill the card.
|
|
*/
|
|
private const MIN_AGE_DAYS = 7;
|
|
|
|
private const LIMIT = 10;
|
|
|
|
public function __construct(
|
|
private readonly BookingEditDraftRepository $draftRepository,
|
|
private readonly UrlGeneratorInterface $urlGenerator,
|
|
) {
|
|
}
|
|
|
|
public function getRequiredRole(): string
|
|
{
|
|
return Role::CUSTOMER_EXPERT;
|
|
}
|
|
|
|
public function getPriority(): int
|
|
{
|
|
return 95;
|
|
}
|
|
|
|
public function build(): ?DashboardWidget
|
|
{
|
|
$drafts = $this->draftRepository->findStuck(self::MIN_AGE_DAYS, self::LIMIT);
|
|
|
|
return new DashboardWidget(
|
|
'Festhängende Buchungsänderungen',
|
|
array_map(fn (BookingEditDraft $draft): DashboardWidgetEntry => new DashboardWidgetEntry(
|
|
$this->label($draft),
|
|
$this->urlGenerator->generate('app_admin_bookingeditdraft_show', ['id' => $draft->getId()]),
|
|
'edit',
|
|
), $drafts),
|
|
'Keine festhängenden Buchungsänderungen.',
|
|
$this->urlGenerator->generate('app_admin_bookingeditdraft'),
|
|
'Alle Buchungsentwürfe',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Names the booking, how long it has been stuck, and when the customer last tried.
|
|
*
|
|
* The gap between the two is what distinguishes an abandoned edit from somebody still trying
|
|
* every few days and getting nowhere.
|
|
*/
|
|
private function label(BookingEditDraft $draft): string
|
|
{
|
|
$days = $draft->getCreatedAt()->diff(new \DateTimeImmutable())->days ?? 0;
|
|
|
|
return sprintf(
|
|
'Vorgang %s (%s) — seit %d Tagen, zuletzt %s',
|
|
$draft->getBookingNumber() ?? $draft->getBookingId(),
|
|
(string) $draft->getUser()->getEmail(),
|
|
$days,
|
|
$draft->getUpdatedAt()->format('d.m.Y'),
|
|
);
|
|
}
|
|
}
|