feat: add admin views, a booking horizon and confirmation modals
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
8caf618950
commit
1a3640b15c
27 files changed
+1269
-62
No files matched your search
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Booking\Horizon;
|
||||
use App\Booking\RecurrenceRule;
|
||||
use App\Booking\TimeRange;
|
||||
use App\Entity\BookingSeries;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
@@ -20,6 +20,13 @@ final class BookingRequest
|
||||
#[Assert\GreaterThanOrEqual('today', message: 'Für vergangene Tage kann nicht gebucht werden.')]
|
||||
public ?\DateTimeImmutable $date = null;
|
||||
|
||||
/**
|
||||
* Lifts the Horizon cap for this request. Set by the controller for ROLE_ADMIN -- a DTO
|
||||
* cannot reach the security container, and giving it one would make these rules untestable
|
||||
* in isolation.
|
||||
*/
|
||||
public bool $unlimitedHorizon = false;
|
||||
|
||||
public bool $allDay = true;
|
||||
|
||||
public ?\DateTimeImmutable $startTime = null;
|
||||
@@ -85,6 +92,8 @@ final class BookingRequest
|
||||
}
|
||||
}
|
||||
|
||||
$this->validateHorizon($context);
|
||||
|
||||
if (!$this->repeats()) {
|
||||
return;
|
||||
}
|
||||
@@ -101,14 +110,31 @@ final class BookingRequest
|
||||
return;
|
||||
}
|
||||
|
||||
$latest = ($this->date ?? new \DateTimeImmutable('today'))->modify(\sprintf('+%d months', BookingSeries::MAX_MONTHS));
|
||||
if ($this->until > $latest) {
|
||||
$context->buildViolation(\sprintf('Eine Serie darf höchstens %d Monate laufen.', BookingSeries::MAX_MONTHS))
|
||||
if (!$this->unlimitedHorizon && $this->until > Horizon::latest()) {
|
||||
$context->buildViolation(\sprintf('Eine Serie darf höchstens %d Monate in die Zukunft laufen.', Horizon::MONTHS))
|
||||
->atPath('until')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The booking horizon, measured from today rather than from the chosen day -- see Horizon.
|
||||
* Not an attribute constraint because it has to be liftable per role, which an attribute
|
||||
* cannot express.
|
||||
*/
|
||||
private function validateHorizon(ExecutionContextInterface $context): void
|
||||
{
|
||||
if ($this->unlimitedHorizon || null === $this->date) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->date > Horizon::latest()) {
|
||||
$context->buildViolation(\sprintf('Es kann höchstens %d Monate im Voraus gebucht werden.', Horizon::MONTHS))
|
||||
->atPath('date')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
private static function timeOf(int $minutes): \DateTimeImmutable
|
||||
{
|
||||
// 24:00 has no representation as a time-of-day, so an all-day end never reaches here.
|
||||
|
||||
Reference in new issue
Block a user