Skip to content

Commit bb7d5a3

Browse files
Timeline: Visualize future rotations
- Introduce class FutureEntry
1 parent e72f8d7 commit bb7d5a3

File tree

4 files changed

+102
-28
lines changed

4 files changed

+102
-28
lines changed

library/Notifications/Widget/TimeGrid/BaseGrid.php

+40-28
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use DateInterval;
88
use DateTime;
99
use Generator;
10+
use Icinga\Module\Notifications\Widget\Timeline\FutureEntry;
1011
use ipl\Html\Attributes;
1112
use ipl\Html\BaseHtmlElement;
1213
use ipl\Html\HtmlElement;
@@ -425,46 +426,57 @@ final protected function yieldFixedEntries(Traversable $entries): Generator
425426
$lastRow = $rowStart;
426427
}
427428

428-
$actualStart = Util::roundToNearestThirtyMinute($entry->getStart());
429-
if ($actualStart < $gridStartsAt) {
430-
$colStart = 0;
431-
} else {
432-
$colStart = Util::diffHours($gridStartsAt, $actualStart) * 2;
433-
}
434-
435-
$actualEnd = Util::roundToNearestThirtyMinute($entry->getEnd());
436-
if ($actualEnd > $gridEndsAt) {
429+
if ($entry instanceof FutureEntry) {
430+
$colStart = ($this->getNoOfVisuallyConnectedHours() / 24 + 1) * - 1;
437431
$colEnd = $gridBorderAt;
438432
} else {
439-
$colEnd = Util::diffHours($gridStartsAt, $actualEnd) * 2;
440-
}
433+
$actualStart = Util::roundToNearestThirtyMinute($entry->getStart());
434+
if ($actualStart < $gridStartsAt) {
435+
$colStart = 0;
436+
} else {
437+
$colStart = Util::diffHours($gridStartsAt, $actualStart) * 2;
438+
}
439+
440+
$actualEnd = Util::roundToNearestThirtyMinute($entry->getEnd());
441+
if ($actualEnd > $gridEndsAt) {
442+
$colEnd = $gridBorderAt;
443+
} else {
444+
$colEnd = Util::diffHours($gridStartsAt, $actualEnd) * 2;
445+
}
441446

442-
if ($colStart > $gridBorderAt || $colEnd === $colStart) {
443-
throw new LogicException(sprintf(
444-
'Invalid entry (%d) position: %s to %s. Grid dimension: %s to %s',
445-
$entry->getId(),
446-
$actualStart->format('Y-m-d H:i:s'),
447-
$actualEnd->format('Y-m-d H:i:s'),
448-
$gridStartsAt->format('Y-m-d'),
449-
$gridEndsAt->format('Y-m-d')
450-
));
447+
if ($colStart > $gridBorderAt || $colEnd === $colStart) {
448+
throw new LogicException(sprintf(
449+
'Invalid entry (%d) position: %s to %s. Grid dimension: %s to %s',
450+
$entry->getId(),
451+
$actualStart->format('Y-m-d H:i:s'),
452+
$actualEnd->format('Y-m-d H:i:s'),
453+
$gridStartsAt->format('Y-m-d'),
454+
$gridEndsAt->format('Y-m-d')
455+
));
456+
}
457+
458+
$colStart++;
451459
}
452460

453461
$gridArea = $this->getGridArea(
454462
$rowStart,
455463
$rowStart + 1,
456-
$colStart + 1,
464+
$colStart,
457465
$colEnd + 1
458466
);
459467

460-
$fromPrevGrid = $gridStartsAt > $entry->getStart();
461-
$toNextGrid = $gridEndsAt < $entry->getEnd();
462-
if ($fromPrevGrid && $toNextGrid) {
463-
$entry->setContinuationType(Entry::ACROSS_GRID);
464-
} elseif ($fromPrevGrid) {
465-
$entry->setContinuationType(Entry::FROM_PREV_GRID);
466-
} elseif ($toNextGrid) {
468+
if ($entry instanceof FutureEntry) {
467469
$entry->setContinuationType(Entry::TO_NEXT_GRID);
470+
} else {
471+
$fromPrevGrid = $gridStartsAt > $entry->getStart();
472+
$toNextGrid = $gridEndsAt < $entry->getEnd();
473+
if ($fromPrevGrid && $toNextGrid) {
474+
$entry->setContinuationType(Entry::ACROSS_GRID);
475+
} elseif ($fromPrevGrid) {
476+
$entry->setContinuationType(Entry::FROM_PREV_GRID);
477+
} elseif ($toNextGrid) {
478+
$entry->setContinuationType(Entry::TO_NEXT_GRID);
479+
}
468480
}
469481

470482
yield $gridArea => $entry;

library/Notifications/Widget/Timeline.php

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Icinga\Module\Notifications\Widget\TimeGrid\EntryProvider;
1313
use Icinga\Module\Notifications\Widget\TimeGrid\GridStep;
1414
use Icinga\Module\Notifications\Widget\Timeline\Entry;
15+
use Icinga\Module\Notifications\Widget\Timeline\FutureEntry;
1516
use Icinga\Module\Notifications\Widget\Timeline\MinimalGrid;
1617
use Icinga\Module\Notifications\Widget\Timeline\Rotation;
1718
use ipl\Html\Attributes;
@@ -169,7 +170,9 @@ public function getEntries(): Traversable
169170

170171
$occupiedCells = [];
171172
foreach ($rotations as $rotation) {
173+
$entryFound = false;
172174
foreach ($rotation->fetchTimeperiodEntries($this->start, $this->getGrid()->getGridEnd()) as $entry) {
175+
$entryFound = true;
173176
if (! $this->minimalLayout) {
174177
$entry->setPosition($maxPriority - $rotation->getPriority());
175178

@@ -178,6 +181,10 @@ public function getEntries(): Traversable
178181

179182
$occupiedCells += $getDesiredCells($entry);
180183
}
184+
185+
if (! $entryFound && ! $this->minimalLayout) {
186+
yield (new FutureEntry(0))->setPosition($maxPriority - $rotation->getPriority());
187+
}
181188
}
182189

183190
$entryToCellsMap = new SplObjectStorage();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Icinga\Module\Notifications\Widget\Timeline;
4+
5+
use Icinga\Module\Notifications\Widget\TimeGrid\Entry;
6+
use ipl\Html\Attributes;
7+
use ipl\Html\BaseHtmlElement;
8+
use ipl\I18n\Translation;
9+
use ipl\Web\Widget\Icon;
10+
11+
class FutureEntry extends Entry
12+
{
13+
use Translation;
14+
15+
public function getColor(int $transparency): string
16+
{
17+
return sprintf('~"hsl(%d %d%% 50%% / %d%%)"', 166, 90, $transparency);
18+
}
19+
20+
protected function assembleContainer(BaseHtmlElement $container): void
21+
{
22+
$this
23+
->addHtml(new Icon('angle-right'))
24+
->addAttributes(new Attributes([
25+
'class' => 'future',
26+
'title' => $this->translate('rotation starts in the future')
27+
]));
28+
}
29+
}

public/css/timeline.less

+26
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@
5858
.text-ellipsis();
5959
}
6060
}
61+
62+
&.future {
63+
display: flex;
64+
align-items: center;
65+
justify-content: center;
66+
67+
position: relative;
68+
padding-left: 6px; // 2px before + 1px border + 2px after + 1px border
69+
70+
&:before,
71+
&:after {
72+
content: '';
73+
display: block;
74+
position: absolute;
75+
border: 1px solid var(--entry-border-color);
76+
border-right: transparent;
77+
height: ~"calc(100% + 2px)"; // border top and bottom
78+
width: 100%;
79+
left: 2px;
80+
.rounded-corners(0.25em);
81+
}
82+
83+
&:after {
84+
left: 5px; // 2px before + 1px border + 2px after
85+
}
86+
}
6187
}
6288

6389
&::after {

0 commit comments

Comments
 (0)