Skip to content

Commit 593900d

Browse files
committed
Dashboard to show all favorites
1 parent 4099219 commit 593900d

File tree

7 files changed

+218
-2
lines changed

7 files changed

+218
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
/* Icinga for Kubernetes Web | (c) 2025 Icinga GmbH | AGPLv3 */
4+
5+
namespace Icinga\Module\Kubernetes\Controllers;
6+
7+
use Icinga\Module\Kubernetes\Common\Auth;
8+
use Icinga\Module\Kubernetes\Common\Database;
9+
use Icinga\Module\Kubernetes\Model\Favorite;
10+
use Icinga\Module\Kubernetes\Web\Controller;
11+
use Icinga\Module\Kubernetes\Web\Factory;
12+
use Icinga\Module\Kubernetes\Web\FavoriteDashboard;
13+
use Icinga\Web\Widget\Dashboard;
14+
use Icinga\Web\Widget\Dashboard\Dashlet;
15+
use Icinga\Web\Widget\Dashboard\Pane;
16+
use ipl\Stdlib\Filter;
17+
18+
class FavoritesController extends Controller
19+
{
20+
const array FAVORABLE_KINDS = [
21+
'cronjob',
22+
'daemonset',
23+
'deployment',
24+
'ingress',
25+
'job',
26+
'namespace',
27+
'node',
28+
'persistentvolumeclaim',
29+
'persistentvolume',
30+
'pod',
31+
'replicaset',
32+
'service',
33+
'statefulset'
34+
];
35+
36+
public function indexAction(): void
37+
{
38+
$this->addTitleTab('Favorites');
39+
$dashboard = new Dashboard();
40+
$pane = (new Pane('favorites'))->setTitle('Favorites');
41+
$dashboard->addPane($pane);
42+
43+
foreach (self::FAVORABLE_KINDS as $kind) {
44+
$hasFavorites = Favorite::on(Database::connection())->filter(
45+
Filter::all(
46+
Filter::equal('kind', $kind),
47+
Filter::equal('username', Auth::getInstance()->getUser()->getUsername())
48+
)
49+
)->first() !== null;
50+
if ($hasFavorites) {
51+
$dashlet = new Dashlet(Factory::createTitle($kind), Factory::createListUrl($kind) . '?view=minimal&show-favorites=y&sort=favorite.priority desc', $pane);
52+
$pane->addDashlet($dashlet);
53+
}
54+
}
55+
56+
$dashboard->activate('favorites');
57+
$this->addContent(new FavoriteDashboard($dashboard));
58+
}
59+
}

configuration.php

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717

1818
$priority = 0;
1919

20+
$section->add(
21+
N_('All Favorites'),
22+
[
23+
'description' => $this->translate('All Favorites'),
24+
'url' => 'kubernetes/favorites',
25+
'priority' => $priority++
26+
]
27+
);
28+
2029
$section->add(
2130
N_('Cluster Services'),
2231
[

library/Kubernetes/Web/Factory.php

+24-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public static function canonicalizeKind(string $kind): string
4141
return 'persistentvolumeclaim';
4242
}
4343

44-
return strtolower(str_replace(['_', '-'], '', $kind));
44+
return strtolower(str_replace([' ', '_', '-'], '', $kind));
4545
}
4646

4747
public static function createIcon(string $kind): ?ValidHtml
@@ -284,6 +284,29 @@ public static function createListUrl(string $kind): ?Url
284284
return null;
285285
}
286286

287+
public static function createTitle(string $kind): string
288+
{
289+
return match ($kind) {
290+
'configmap' => 'Config Maps',
291+
'cronjob' => 'Cron Jobs',
292+
'daemonset' => 'Daemon Sets',
293+
'deployment' => 'Deployments',
294+
'event' => 'Events',
295+
'ingress' => 'Ingresses',
296+
'job' => 'Jobs',
297+
'namespace' => 'Namespaces',
298+
'node' => 'Nodes',
299+
'persistentvolume' => 'Persistent Volumes',
300+
'persistentvolumeclaim' => 'Persistent Volume Claims',
301+
'pod' => 'Pods',
302+
'replicaset' => 'Replica Sets',
303+
'secret' => 'Secrets',
304+
'service' => 'Services',
305+
'statefulset' => 'Stateful Sets',
306+
default => null,
307+
};
308+
}
309+
287310
public static function getKindFromModel(Model $model): string
288311
{
289312
$kind = match (true) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/* Icinga for Kubernetes Web | (c) 2025 Icinga GmbH | AGPLv3 */
4+
5+
namespace Icinga\Module\Kubernetes\Web;
6+
7+
use Icinga\Web\Widget\Dashboard;
8+
use ipl\Html\BaseHtmlElement;
9+
10+
class FavoriteDashboard extends BaseHtmlElement
11+
{
12+
/** @var Dashboard The dashboard to show all favorites of all resources. */
13+
protected Dashboard $dashboard;
14+
15+
protected $tag = 'div';
16+
17+
protected $defaultAttributes = ['class' => 'favorite-dashboard'];
18+
19+
public function __construct(Dashboard $dashboard)
20+
{
21+
$this->dashboard = $dashboard;
22+
}
23+
24+
public function assemble()
25+
{
26+
foreach ($this->dashboard->getActivePane()->getDashlets() as $dashlet) {
27+
$this->add(new FavoriteDashlet($dashlet));
28+
}
29+
}
30+
}
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/* Icinga for Kubernetes Web | (c) 2025 Icinga GmbH | AGPLv3 */
4+
5+
namespace Icinga\Module\Kubernetes\Web;
6+
7+
use Icinga\Web\Widget\Dashboard\Dashlet;
8+
use ipl\Html\Attributes;
9+
use ipl\Html\BaseHtmlElement;
10+
use ipl\Html\Html;
11+
12+
class FavoriteDashlet extends BaseHtmlElement
13+
{
14+
/** @var Dashlet The dashlet to show all favorites of a resource. */
15+
protected Dashlet $dashlet;
16+
17+
protected $tag = 'div';
18+
19+
protected $defaultAttributes = [
20+
'class' => 'container',
21+
'data-last-update' => -1,
22+
'data-icinga-refresh' => 60
23+
];
24+
25+
public function __construct(Dashlet $dashlet)
26+
{
27+
$this->dashlet = $dashlet;
28+
}
29+
30+
public function assemble()
31+
{
32+
$url = $this->dashlet->getUrl()->setParam('showCompact');
33+
$fullUrl = $url->getUrlWithout(['showCompact', 'view']);
34+
$tooltip = sprintf('Show %s', $this->dashlet->getTitle());
35+
$progressLabel = $this->dashlet->getProgressLabe();
36+
37+
$this->addAttributes(['data-icinga-url' => $url]);
38+
39+
$this->addHtml(
40+
Html::tag(
41+
'h1',
42+
null,
43+
Html::tag(
44+
'a',
45+
Attributes::create([
46+
'href' => $fullUrl,
47+
'aria-label' => $tooltip,
48+
'title' => $tooltip,
49+
'data-base-target' => 'col1'
50+
]),
51+
$this->dashlet->getName()
52+
)
53+
),
54+
Html::tag(
55+
'p',
56+
Attributes::create(['class' => 'progress-label']),
57+
[
58+
$progressLabel,
59+
Html::tag('span', null, '.'),
60+
Html::tag('span', null, '.'),
61+
Html::tag('span', null, '.')
62+
]
63+
),
64+
Html::tag('div', Attributes::create(['class' => 'content']))
65+
);
66+
}
67+
}

public/css/common.less

+28
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,31 @@ pre {
164164
}
165165
}
166166
}
167+
168+
.favorite-dashboard {
169+
display: flex;
170+
flex-wrap: wrap;
171+
gap: 1em;
172+
173+
.container {
174+
padding: 1em;
175+
width: 100%;
176+
177+
> .content {
178+
padding: 0;
179+
}
180+
181+
> .progress-label {
182+
margin: 0;
183+
184+
span {
185+
font-size: 0.75em;
186+
}
187+
}
188+
}
189+
}
190+
191+
#layout.wide-layout:not(.twocols) .favorite-dashboard > .container:not(:only-child),
192+
#layout.default-layout:not(.twocols) .favorite-dashboard > .container:not(:only-child) {
193+
width: ~"calc(50% - 0.5em)";
194+
}

public/js/action-list.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
return;
9999
}
100100

101-
let dashboard = list.closest('.dashboard');
101+
let dashboard = list.closest('.dashboard') || list.closest('.favorite-dashboard');
102102
if (dashboard) {
103103
_this.clearDashboardSelections(dashboard, list);
104104
}

0 commit comments

Comments
 (0)