Skip to content

Commit

Permalink
schedule: Update detail to reflect the new redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
nilmerg committed May 10, 2024
1 parent 3ec97d3 commit 99b7187
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 143 deletions.
83 changes: 79 additions & 4 deletions application/controllers/ScheduleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
use Icinga\Module\Notifications\Common\Database;
use Icinga\Module\Notifications\Common\Links;
use Icinga\Module\Notifications\Forms\EntryForm;
use Icinga\Module\Notifications\Forms\RotationConfigForm;
use Icinga\Module\Notifications\Forms\ScheduleForm;
use Icinga\Module\Notifications\Model\Schedule;
use Icinga\Module\Notifications\Widget\Calendar\Controls;
use Icinga\Module\Notifications\Widget\RecipientSuggestions;
use Icinga\Module\Notifications\Widget\Schedule as ScheduleWidget;
use ipl\Html\Form;
Expand Down Expand Up @@ -37,20 +37,26 @@ public function indexAction(): void
$this->addTitleTab(sprintf(t('Schedule: %s'), $schedule->name));

$this->controls->addHtml(
Html::tag('h2', null, $schedule->name),
(new ButtonLink(
null,
Links::scheduleSettings($id),
'cog'
))->openInModal(),
(new ButtonLink(
$this->translate('Add Rotation'),
Links::rotationAdd($id),
'plus'
))->openInModal()
);

$this->controls->addAttributes(['class' => 'schedule-controls']);
$this->controls->addAttributes(['class' => 'schedule-detail-controls']);

$calendarControls = (new Controls())
$scheduleControls = (new ScheduleWidget\Controls())
->setAction(Url::fromRequest()->getAbsoluteUrl())
->handleRequest($this->getServerRequest());

$this->addContent(new ScheduleWidget($calendarControls, $schedule));
$this->addContent(new ScheduleWidget($schedule, $scheduleControls));
}

public function settingsAction(): void
Expand Down Expand Up @@ -141,6 +147,75 @@ public function addEntryAction(): void
}
}

public function addRotationAction(): void
{
$scheduleId = (int) $this->params->getRequired('schedule');

$form = new RotationConfigForm(Database::get());
$form->setAction($this->getRequest()->getUrl()->setParam('showCompact')->getAbsoluteUrl());
$form->setSuggestionUrl(Url::fromPath('notifications/schedule/suggest-recipient'));
$form->on(RotationConfigForm::ON_SENT, function ($form) {
if (! $form->hasBeenSubmitted()) {
foreach ($form->getPartUpdates() as $update) {
if (! is_array($update)) {
$update = [$update];
}

$this->addPart(...$update);
}
}
});
$form->on(RotationConfigForm::ON_SUCCESS, function (RotationConfigForm $form) use ($scheduleId) {
$form->addRotation($scheduleId);
$this->closeModalAndRefreshRelatedView(Links::schedule($scheduleId));
});

$form->handleRequest($this->getServerRequest());

if (empty($this->parts)) {
$this->setTitle($this->translate('Add Rotation'));
$this->addContent($form);
}
}

public function editRotationAction(): void
{
$id = (int) $this->params->getRequired('id');

$form = new RotationConfigForm(Database::get());
$form->disableModeSelection();
$form->setShowRemoveButton();
$form->loadRotation($id);
$form->setSubmitLabel($this->translate('Save Changes'));
$form->setAction($this->getRequest()->getUrl()->getAbsoluteUrl());
$form->setSuggestionUrl(Url::fromPath('notifications/schedule/suggest-recipient'));
$form->on(RotationConfigForm::ON_SUCCESS, function (RotationConfigForm $form) use ($id) {
$form->editRotation($id);
$this->redirectNow('__CLOSE__');
});
$form->on(RotationConfigForm::ON_SENT, function (RotationConfigForm $form) use ($id) {
if ($form->hasBeenRemoved()) {
$form->removeRotation($id);
$this->redirectNow('__CLOSE__');
} elseif (! $form->hasBeenSubmitted()) {
foreach ($form->getPartUpdates() as $update) {
if (! is_array($update)) {
$update = [$update];
}

$this->addPart(...$update);
}
}
});

$form->handleRequest($this->getServerRequest());

if (empty($this->parts)) {
$this->setTitle($this->translate('Edit Rotation'));
$this->addContent($form);
}
}

public function editEntryAction(): void
{
$entryId = (int) $this->params->getRequired('id');
Expand Down
153 changes: 38 additions & 115 deletions library/Notifications/Widget/Schedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,14 @@

namespace Icinga\Module\Notifications\Widget;

use DateTimeZone;
use Icinga\Module\Notifications\Common\Database;
use Icinga\Module\Notifications\Model\ScheduleMember;
use Icinga\Module\Notifications\Model\TimeperiodEntry;
use Icinga\Module\Notifications\Widget\Calendar\Attendee;
use Icinga\Module\Notifications\Widget\Calendar\Controls;
use Icinga\Module\Notifications\Widget\Calendar\Entry;
use Icinga\Module\Notifications\Widget\Schedule\Controls;
use Icinga\Module\Notifications\Widget\Timeline\Rotation;
use Icinga\Util\Csp;
use ipl\Html\Attributes;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
use ipl\Stdlib\Filter;
use ipl\Web\Common\BaseTarget;
use ipl\Web\Style;
use ipl\Web\Url;
use ipl\Web\Widget\Icon;
use ipl\Web\Widget\Link;

class Schedule extends BaseHtmlElement
{
Expand All @@ -36,122 +27,54 @@ class Schedule extends BaseHtmlElement
/** @var Controls */
protected $controls;

public function __construct(Controls $controls, ?\Icinga\Module\Notifications\Model\Schedule $schedule)
/**
* Create a new Schedule
*
* @param \Icinga\Module\Notifications\Model\Schedule $schedule
* @param Controls $controls
*/
public function __construct(\Icinga\Module\Notifications\Model\Schedule $schedule, Controls $controls)
{
$this->schedule = $schedule;
$this->controls = $controls;
}

protected function assembleCalendar(Calendar $calendar): void
/**
* Assemble the timeline
*
* @param Timeline $timeline
*/
protected function assembleTimeline(Timeline $timeline): void
{
$calendar->setAddEntryUrl(Url::fromPath(
'notifications/schedule/add-entry',
['schedule' => $this->schedule->id]
));

$calendar->setUrl(Url::fromPath(
'notifications/schedules',
['schedule' => $this->schedule->id]
));

$db = Database::get();
$entries = TimeperiodEntry::on($db)
->filter(Filter::equal('timeperiod.schedule.id', $this->schedule->id))
->orderBy(['start_time', 'timeperiod_id']);

$entryFilter = Filter::any(
Filter::all( // all entries that start in the shown range
Filter::greaterThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::all( // all entries that end in the shown range
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::lessThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::all( // all entries that start before and end after the shown range
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::none( // all entries that are repeated and may still occur in the shown range
Filter::lessThanOrEqual('until_time', $calendar->getGrid()->getGridStart()->getTimestamp())
),
Filter::all( // all entries that are repeated endlessly and already started in the past
Filter::unlike('until_time', '*'),
Filter::like('rrule', '*'),
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp())
)
);

foreach ($entries->filter($entryFilter) as $entry) {
$members = ScheduleMember::on($db)
->with(['timeperiod', 'contact', 'contactgroup'])
->filter(Filter::equal('timeperiod_id', $entry->timeperiod_id))
->orderBy(['contact_id', 'contactgroup_id']);

foreach ($members as $member) {
if ($member->contact_id !== null) {
$attendee = new Attendee($member->contact->full_name);
} else { // $member->contactgroup_id !== null
$attendee = new Attendee($member->contactgroup->name);
$attendee->setIcon('users');
}

$calendar->addEntry(
(new Entry($entry->id))
->setDescription($entry->description)
->setRecurrencyRule($entry->rrule)
->setStart((clone $entry->start_time)->setTimezone(new DateTimeZone($entry->timezone)))
->setEnd((clone $entry->end_time)->setTimezone(new DateTimeZone($entry->timezone)))
->setUrl(Url::fromPath('notifications/schedule/edit-entry', [
'id' => $entry->id,
'schedule' => $this->schedule->id
]))
->setAttendee($attendee)
);
}
foreach ($this->schedule->rotation->with('timeperiod') as $rotation) {
$timeline->addRotation(new Rotation($rotation));
}
}

public function assemble()
/**
* Create the timeline
*
* @return Timeline
*/
protected function createTimeline(): Timeline
{
$calendar = (new Calendar())
->setControls($this->controls)
->setStyle(
(new Style())
->setNonce(Csp::getStyleNonce())
->setModule('notifications')
);

$this->setBaseTarget('entry-form');
if ($this->controls->getBaseTarget() === null) {
$this->controls->setBaseTarget('_self');
}
$timeline = new Timeline($this->controls->getStartDate(), $this->controls->getNumberOfDays());
$timeline->setStyle(
(new Style())
->setNonce(Csp::getStyleNonce())
->setModule('notifications')
);

$scheduleHeader = new HtmlElement('div', Attributes::create(['class' => 'schedule-header']));
if ($this->schedule !== null) {
$this->assembleCalendar($calendar);
$scheduleHeader->addHtml(
new Link(
[
new Icon('plus'),
t('Add new entry')
],
Url::fromPath('notifications/schedule/add-entry', ['schedule' => $this->schedule->id]),
['class' => 'button-link']
)
);
}
$this->assembleTimeline($timeline);

$scheduleContainer = new HtmlElement('div', Attributes::create(['class' => 'schedule-container']));
$scheduleContainer->addHtml($calendar);
$scheduleContainer->addHtml(new HtmlElement(
'div',
Attributes::create([
'id' => 'entry-form',
'class' => 'entry-form container'
])
));
return $timeline;
}

$this->addHtml($scheduleHeader, $scheduleContainer);
protected function assemble()
{
$this->addHtml(
new HtmlElement('div', Attributes::create(['class' => 'schedule-header']), $this->controls),
new HtmlElement('div', Attributes::create(['class' => 'schedule-container']), $this->createTimeline())
);
}
}
73 changes: 73 additions & 0 deletions library/Notifications/Widget/Schedule/Controls.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/* Icinga Notifications Web | (c) 2024 Icinga GmbH | GPLv2 */

namespace Icinga\Module\Notifications\Widget\Schedule;

use DateTime;
use ipl\Html\Attributes;
use ipl\Html\Form;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\I18n\Translation;

class Controls extends Form
{
use Translation;

protected $method = 'GET';

protected $defaultAttributes = ['class' => 'schedule-controls'];

/**
* Get the number of days the user wants to see
*
* @return int
*/
public function getNumberOfDays(): int
{
return (int) $this->getPopulatedValue('days', '7');
}

/**
* Get the start date where the user wants the schedule to begin
*
* @return DateTime
*/
public function getStartDate(): DateTime
{
return (new DateTime())->setTime(0, 0);
}

protected function assemble()
{
$param = 'days';
$options = [
1 => $this->translate('Day'),
7 => $this->translate('Week'),
14 => $this->translate('2 Weeks'),
31 => $this->translate('Month')
];

$viewModeSwitcher = HtmlElement::create('fieldset', ['class' => 'view-mode-switcher']);
foreach ($options as $value => $label) {
$input = $this->createElement('input', $param, [
'class' => 'autosubmit',
'type' => 'radio',
'id' => $param . '-' . $value,
'value' => $value
]);

$input->getAttributes()->registerAttributeCallback('checked', function () use ($value) {
return $value === $this->getNumberOfDays();
});

$viewModeSwitcher->addHtml(
$input,
new HtmlElement('label', Attributes::create(['for' => $param . '-' . $value]), Text::create($label))
);
}

$this->addHtml($viewModeSwitcher);
}
}
Loading

0 comments on commit 99b7187

Please sign in to comment.