Skip to content

Commit 2fa84ef

Browse files
committed
schedule: Update detail to reflect the new redesign
1 parent e643222 commit 2fa84ef

File tree

4 files changed

+210
-143
lines changed

4 files changed

+210
-143
lines changed

application/controllers/ScheduleController.php

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
use Icinga\Module\Notifications\Common\Database;
88
use Icinga\Module\Notifications\Common\Links;
99
use Icinga\Module\Notifications\Forms\EntryForm;
10+
use Icinga\Module\Notifications\Forms\RotationConfigForm;
1011
use Icinga\Module\Notifications\Forms\ScheduleForm;
1112
use Icinga\Module\Notifications\Model\Schedule;
12-
use Icinga\Module\Notifications\Widget\Calendar\Controls;
1313
use Icinga\Module\Notifications\Widget\RecipientSuggestions;
1414
use Icinga\Module\Notifications\Widget\Schedule as ScheduleWidget;
1515
use ipl\Html\Form;
@@ -37,20 +37,26 @@ public function indexAction(): void
3737
$this->addTitleTab(sprintf(t('Schedule: %s'), $schedule->name));
3838

3939
$this->controls->addHtml(
40+
Html::tag('h2', null, $schedule->name),
4041
(new ButtonLink(
4142
null,
4243
Links::scheduleSettings($id),
4344
'cog'
45+
))->openInModal(),
46+
(new ButtonLink(
47+
$this->translate('Add Rotation'),
48+
Links::rotationAdd($id),
49+
'plus'
4450
))->openInModal()
4551
);
4652

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

49-
$calendarControls = (new Controls())
55+
$scheduleControls = (new ScheduleWidget\Controls())
5056
->setAction(Url::fromRequest()->getAbsoluteUrl())
5157
->handleRequest($this->getServerRequest());
5258

53-
$this->addContent(new ScheduleWidget($calendarControls, $schedule));
59+
$this->addContent(new ScheduleWidget($schedule, $scheduleControls));
5460
}
5561

5662
public function settingsAction(): void
@@ -141,6 +147,75 @@ public function addEntryAction(): void
141147
}
142148
}
143149

150+
public function addRotationAction(): void
151+
{
152+
$scheduleId = (int) $this->params->getRequired('schedule');
153+
154+
$form = new RotationConfigForm(Database::get());
155+
$form->setAction($this->getRequest()->getUrl()->setParam('showCompact')->getAbsoluteUrl());
156+
$form->setSuggestionUrl(Url::fromPath('notifications/schedule/suggest-recipient'));
157+
$form->on(RotationConfigForm::ON_SENT, function ($form) {
158+
if (! $form->hasBeenSubmitted()) {
159+
foreach ($form->getPartUpdates() as $update) {
160+
if (! is_array($update)) {
161+
$update = [$update];
162+
}
163+
164+
$this->addPart(...$update);
165+
}
166+
}
167+
});
168+
$form->on(RotationConfigForm::ON_SUCCESS, function (RotationConfigForm $form) use ($scheduleId) {
169+
$form->addRotation($scheduleId);
170+
$this->closeModalAndRefreshRelatedView(Links::schedule($scheduleId));
171+
});
172+
173+
$form->handleRequest($this->getServerRequest());
174+
175+
if (empty($this->parts)) {
176+
$this->setTitle($this->translate('Add Rotation'));
177+
$this->addContent($form);
178+
}
179+
}
180+
181+
public function editRotationAction(): void
182+
{
183+
$id = (int) $this->params->getRequired('id');
184+
185+
$form = new RotationConfigForm(Database::get());
186+
$form->disableModeSelection();
187+
$form->setShowRemoveButton();
188+
$form->loadRotation($id);
189+
$form->setSubmitLabel($this->translate('Save Changes'));
190+
$form->setAction($this->getRequest()->getUrl()->getAbsoluteUrl());
191+
$form->setSuggestionUrl(Url::fromPath('notifications/schedule/suggest-recipient'));
192+
$form->on(RotationConfigForm::ON_SUCCESS, function (RotationConfigForm $form) use ($id) {
193+
$form->editRotation($id);
194+
$this->redirectNow('__CLOSE__');
195+
});
196+
$form->on(RotationConfigForm::ON_SENT, function (RotationConfigForm $form) use ($id) {
197+
if ($form->hasBeenRemoved()) {
198+
$form->removeRotation($id);
199+
$this->redirectNow('__CLOSE__');
200+
} elseif (! $form->hasBeenSubmitted()) {
201+
foreach ($form->getPartUpdates() as $update) {
202+
if (! is_array($update)) {
203+
$update = [$update];
204+
}
205+
206+
$this->addPart(...$update);
207+
}
208+
}
209+
});
210+
211+
$form->handleRequest($this->getServerRequest());
212+
213+
if (empty($this->parts)) {
214+
$this->setTitle($this->translate('Edit Rotation'));
215+
$this->addContent($form);
216+
}
217+
}
218+
144219
public function editEntryAction(): void
145220
{
146221
$entryId = (int) $this->params->getRequired('id');

library/Notifications/Widget/Schedule.php

Lines changed: 38 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,14 @@
44

55
namespace Icinga\Module\Notifications\Widget;
66

7-
use DateTimeZone;
8-
use Icinga\Module\Notifications\Common\Database;
9-
use Icinga\Module\Notifications\Model\ScheduleMember;
10-
use Icinga\Module\Notifications\Model\TimeperiodEntry;
11-
use Icinga\Module\Notifications\Widget\Calendar\Attendee;
12-
use Icinga\Module\Notifications\Widget\Calendar\Controls;
13-
use Icinga\Module\Notifications\Widget\Calendar\Entry;
7+
use Icinga\Module\Notifications\Widget\Schedule\Controls;
8+
use Icinga\Module\Notifications\Widget\Timeline\Rotation;
149
use Icinga\Util\Csp;
1510
use ipl\Html\Attributes;
1611
use ipl\Html\BaseHtmlElement;
1712
use ipl\Html\HtmlElement;
18-
use ipl\Stdlib\Filter;
1913
use ipl\Web\Common\BaseTarget;
2014
use ipl\Web\Style;
21-
use ipl\Web\Url;
22-
use ipl\Web\Widget\Icon;
23-
use ipl\Web\Widget\Link;
2415

2516
class Schedule extends BaseHtmlElement
2617
{
@@ -36,122 +27,54 @@ class Schedule extends BaseHtmlElement
3627
/** @var Controls */
3728
protected $controls;
3829

39-
public function __construct(Controls $controls, ?\Icinga\Module\Notifications\Model\Schedule $schedule)
30+
/**
31+
* Create a new Schedule
32+
*
33+
* @param \Icinga\Module\Notifications\Model\Schedule $schedule
34+
* @param Controls $controls
35+
*/
36+
public function __construct(\Icinga\Module\Notifications\Model\Schedule $schedule, Controls $controls)
4037
{
4138
$this->schedule = $schedule;
4239
$this->controls = $controls;
4340
}
4441

45-
protected function assembleCalendar(Calendar $calendar): void
42+
/**
43+
* Assemble the timeline
44+
*
45+
* @param Timeline $timeline
46+
*/
47+
protected function assembleTimeline(Timeline $timeline): void
4648
{
47-
$calendar->setAddEntryUrl(Url::fromPath(
48-
'notifications/schedule/add-entry',
49-
['schedule' => $this->schedule->id]
50-
));
51-
52-
$calendar->setUrl(Url::fromPath(
53-
'notifications/schedules',
54-
['schedule' => $this->schedule->id]
55-
));
56-
57-
$db = Database::get();
58-
$entries = TimeperiodEntry::on($db)
59-
->filter(Filter::equal('timeperiod.schedule.id', $this->schedule->id))
60-
->orderBy(['start_time', 'timeperiod_id']);
61-
62-
$entryFilter = Filter::any(
63-
Filter::all( // all entries that start in the shown range
64-
Filter::greaterThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
65-
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
66-
),
67-
Filter::all( // all entries that end in the shown range
68-
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
69-
Filter::lessThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
70-
),
71-
Filter::all( // all entries that start before and end after the shown range
72-
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
73-
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
74-
),
75-
Filter::none( // all entries that are repeated and may still occur in the shown range
76-
Filter::lessThanOrEqual('until_time', $calendar->getGrid()->getGridStart()->getTimestamp())
77-
),
78-
Filter::all( // all entries that are repeated endlessly and already started in the past
79-
Filter::unlike('until_time', '*'),
80-
Filter::like('rrule', '*'),
81-
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp())
82-
)
83-
);
84-
85-
foreach ($entries->filter($entryFilter) as $entry) {
86-
$members = ScheduleMember::on($db)
87-
->with(['timeperiod', 'contact', 'contactgroup'])
88-
->filter(Filter::equal('timeperiod_id', $entry->timeperiod_id))
89-
->orderBy(['contact_id', 'contactgroup_id']);
90-
91-
foreach ($members as $member) {
92-
if ($member->contact_id !== null) {
93-
$attendee = new Attendee($member->contact->full_name);
94-
} else { // $member->contactgroup_id !== null
95-
$attendee = new Attendee($member->contactgroup->name);
96-
$attendee->setIcon('users');
97-
}
98-
99-
$calendar->addEntry(
100-
(new Entry($entry->id))
101-
->setDescription($entry->description)
102-
->setRecurrencyRule($entry->rrule)
103-
->setStart((clone $entry->start_time)->setTimezone(new DateTimeZone($entry->timezone)))
104-
->setEnd((clone $entry->end_time)->setTimezone(new DateTimeZone($entry->timezone)))
105-
->setUrl(Url::fromPath('notifications/schedule/edit-entry', [
106-
'id' => $entry->id,
107-
'schedule' => $this->schedule->id
108-
]))
109-
->setAttendee($attendee)
110-
);
111-
}
49+
foreach ($this->schedule->rotation->with('timeperiod') as $rotation) {
50+
$timeline->addRotation(new Rotation($rotation));
11251
}
11352
}
11453

115-
public function assemble()
54+
/**
55+
* Create the timeline
56+
*
57+
* @return Timeline
58+
*/
59+
protected function createTimeline(): Timeline
11660
{
117-
$calendar = (new Calendar())
118-
->setControls($this->controls)
119-
->setStyle(
120-
(new Style())
121-
->setNonce(Csp::getStyleNonce())
122-
->setModule('notifications')
123-
);
124-
125-
$this->setBaseTarget('entry-form');
126-
if ($this->controls->getBaseTarget() === null) {
127-
$this->controls->setBaseTarget('_self');
128-
}
61+
$timeline = new Timeline($this->controls->getStartDate(), $this->controls->getNumberOfDays());
62+
$timeline->setStyle(
63+
(new Style())
64+
->setNonce(Csp::getStyleNonce())
65+
->setModule('notifications')
66+
);
12967

130-
$scheduleHeader = new HtmlElement('div', Attributes::create(['class' => 'schedule-header']));
131-
if ($this->schedule !== null) {
132-
$this->assembleCalendar($calendar);
133-
$scheduleHeader->addHtml(
134-
new Link(
135-
[
136-
new Icon('plus'),
137-
t('Add new entry')
138-
],
139-
Url::fromPath('notifications/schedule/add-entry', ['schedule' => $this->schedule->id]),
140-
['class' => 'button-link']
141-
)
142-
);
143-
}
68+
$this->assembleTimeline($timeline);
14469

145-
$scheduleContainer = new HtmlElement('div', Attributes::create(['class' => 'schedule-container']));
146-
$scheduleContainer->addHtml($calendar);
147-
$scheduleContainer->addHtml(new HtmlElement(
148-
'div',
149-
Attributes::create([
150-
'id' => 'entry-form',
151-
'class' => 'entry-form container'
152-
])
153-
));
70+
return $timeline;
71+
}
15472

155-
$this->addHtml($scheduleHeader, $scheduleContainer);
73+
protected function assemble()
74+
{
75+
$this->addHtml(
76+
new HtmlElement('div', Attributes::create(['class' => 'schedule-header']), $this->controls),
77+
new HtmlElement('div', Attributes::create(['class' => 'schedule-container']), $this->createTimeline())
78+
);
15679
}
15780
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/* Icinga Notifications Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Notifications\Widget\Schedule;
6+
7+
use DateTime;
8+
use ipl\Html\Attributes;
9+
use ipl\Html\Form;
10+
use ipl\Html\HtmlElement;
11+
use ipl\Html\Text;
12+
use ipl\I18n\Translation;
13+
14+
class Controls extends Form
15+
{
16+
use Translation;
17+
18+
protected $method = 'GET';
19+
20+
protected $defaultAttributes = ['class' => 'schedule-controls'];
21+
22+
/**
23+
* Get the number of days the user wants to see
24+
*
25+
* @return int
26+
*/
27+
public function getNumberOfDays(): int
28+
{
29+
return (int) $this->getPopulatedValue('days', '7');
30+
}
31+
32+
/**
33+
* Get the start date where the user wants the schedule to begin
34+
*
35+
* @return DateTime
36+
*/
37+
public function getStartDate(): DateTime
38+
{
39+
return (new DateTime())->setTime(0, 0);
40+
}
41+
42+
protected function assemble()
43+
{
44+
$param = 'days';
45+
$options = [
46+
1 => $this->translate('Day'),
47+
7 => $this->translate('Week'),
48+
14 => $this->translate('2 Weeks'),
49+
31 => $this->translate('Month')
50+
];
51+
52+
$viewModeSwitcher = HtmlElement::create('fieldset', ['class' => 'view-mode-switcher']);
53+
foreach ($options as $value => $label) {
54+
$input = $this->createElement('input', $param, [
55+
'class' => 'autosubmit',
56+
'type' => 'radio',
57+
'id' => $param . '-' . $value,
58+
'value' => $value
59+
]);
60+
61+
$input->getAttributes()->registerAttributeCallback('checked', function () use ($value) {
62+
return $value === $this->getNumberOfDays();
63+
});
64+
65+
$viewModeSwitcher->addHtml(
66+
$input,
67+
new HtmlElement('label', Attributes::create(['for' => $param . '-' . $value]), Text::create($label))
68+
);
69+
}
70+
71+
$this->addHtml($viewModeSwitcher);
72+
}
73+
}

0 commit comments

Comments
 (0)