feat(sidebar): add indicator for an openable submenu (#5398) [EE-538]
parent
d2d885359f
commit
bbbc61dca9
|
@ -31,10 +31,6 @@ angular.module('portainer').run([
|
|||
HttpRequestHelper.resetAgentHeaders();
|
||||
});
|
||||
|
||||
$state.defaultErrorHandler(function () {
|
||||
// Do not log transitionTo errors
|
||||
});
|
||||
|
||||
// Keep-alive Edge endpoints by sending a ping request every minute
|
||||
$interval(function () {
|
||||
ping(EndpointProvider, SystemService);
|
||||
|
|
|
@ -376,98 +376,6 @@ a[ng-click] {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
ul.sidebar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title.endpoint-name {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a.active {
|
||||
color: #fff;
|
||||
text-indent: 22px;
|
||||
border-left: 3px solid #fff;
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
height: 60px;
|
||||
list-style: none;
|
||||
text-indent: 20px;
|
||||
font-size: 18px;
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
.sidebar-header a {
|
||||
color: #fff;
|
||||
}
|
||||
.sidebar-header a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-header .menu-icon {
|
||||
float: right;
|
||||
padding-right: 28px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) .sidebar-footer-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-footer-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .logo {
|
||||
width: 100%;
|
||||
max-width: 100px;
|
||||
height: 100%;
|
||||
max-height: 35px;
|
||||
margin: 2px 0 2px 20px;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .update-notification {
|
||||
font-size: 14px;
|
||||
padding: 12px;
|
||||
border-radius: 2px;
|
||||
background-color: #ff851b;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .version {
|
||||
font-size: 11px;
|
||||
margin: 11px 20px 0 7px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#sidebar-wrapper {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.sidebar-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#image-layers .btn {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -481,86 +389,6 @@ ul.sidebar .sidebar-list a.active {
|
|||
font-size: 90%;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a.active .menu-icon {
|
||||
text-indent: 25px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
text-indent: 35px;
|
||||
font-size: 12px;
|
||||
color: #b2bfdc;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title {
|
||||
line-height: 36px;
|
||||
}
|
||||
ul.sidebar .sidebar-title .form-control {
|
||||
height: 36px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a,
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a.active {
|
||||
color: #fff;
|
||||
border-left: 3px solid #fff;
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
@media (max-height: 785px) {
|
||||
ul.sidebar .sidebar-title {
|
||||
line-height: 26px;
|
||||
}
|
||||
ul.sidebar .sidebar-title .form-control {
|
||||
height: 26px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
ul.sidebar .sidebar-list {
|
||||
height: 26px;
|
||||
}
|
||||
ul.sidebar .sidebar-list a,
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
font-size: 12px;
|
||||
line-height: 26px;
|
||||
}
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
line-height: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-height: 786px) and (max-height: 924px) {
|
||||
ul.sidebar .sidebar-title {
|
||||
line-height: 30px;
|
||||
}
|
||||
ul.sidebar .sidebar-title .form-control {
|
||||
height: 30px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
ul.sidebar .sidebar-list {
|
||||
height: 30px;
|
||||
}
|
||||
ul.sidebar .sidebar-list a,
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
}
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.margin-sm-top {
|
||||
margin-top: 5px;
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
padding-left: 70px;
|
||||
}
|
||||
}
|
||||
#page-wrapper.open #sidebar-wrapper {
|
||||
left: 150px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hamburg Menu
|
||||
|
@ -254,139 +251,6 @@ div.input-mask {
|
|||
padding-top: 7px;
|
||||
}
|
||||
|
||||
/* #592727 RED */
|
||||
/* #2f5927 GREEN */
|
||||
/* #30426a BLUE (default)*/
|
||||
/* Sidebar background color */
|
||||
/* Sidebar header and footer color */
|
||||
/* Sidebar title text colour */
|
||||
/* Sidebar menu item hover color */
|
||||
/**
|
||||
* Sidebar
|
||||
*/
|
||||
#sidebar-wrapper {
|
||||
background: #30426a;
|
||||
}
|
||||
ul.sidebar .sidebar-main a,
|
||||
.sidebar-footer,
|
||||
ul.sidebar .sidebar-list a:hover,
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator {
|
||||
/* Sidebar header and footer color */
|
||||
background: #2d3e63;
|
||||
}
|
||||
ul.sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
text-indent: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
ul.sidebar li a {
|
||||
color: #fff;
|
||||
display: block;
|
||||
float: left;
|
||||
text-decoration: none;
|
||||
width: 250px;
|
||||
}
|
||||
ul.sidebar .sidebar-main {
|
||||
height: 65px;
|
||||
}
|
||||
ul.sidebar .sidebar-main a {
|
||||
font-size: 18px;
|
||||
line-height: 60px;
|
||||
}
|
||||
ul.sidebar .sidebar-main a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
ul.sidebar .sidebar-main .menu-icon {
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
padding-right: 28px;
|
||||
line-height: 60px;
|
||||
}
|
||||
ul.sidebar .sidebar-title {
|
||||
color: #738bc0;
|
||||
font-size: 12px;
|
||||
height: 35px;
|
||||
line-height: 40px;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.6s ease 0s;
|
||||
}
|
||||
ul.sidebar .sidebar-list {
|
||||
height: 40px;
|
||||
}
|
||||
ul.sidebar .sidebar-list a {
|
||||
text-indent: 25px;
|
||||
font-size: 15px;
|
||||
color: #b2bfdc;
|
||||
line-height: 40px;
|
||||
}
|
||||
ul.sidebar .sidebar-list a:hover {
|
||||
color: #fff;
|
||||
border-left: 3px solid #e99d1a;
|
||||
text-indent: 22px;
|
||||
}
|
||||
ul.sidebar .sidebar-list a:hover .menu-icon {
|
||||
text-indent: 25px;
|
||||
}
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
float: right;
|
||||
padding-right: 29px;
|
||||
line-height: 40px;
|
||||
width: 70px;
|
||||
}
|
||||
#page-wrapper:not(.open) ul.sidebar {
|
||||
bottom: 0;
|
||||
}
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-title {
|
||||
display: none;
|
||||
height: 0px;
|
||||
text-indent: -100px;
|
||||
}
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator {
|
||||
display: block;
|
||||
height: 2px;
|
||||
margin: 13px 0;
|
||||
}
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-list a:hover span {
|
||||
border-left: 3px solid #e99d1a;
|
||||
text-indent: 22px;
|
||||
}
|
||||
#page-wrapper:not(.open) .sidebar-footer {
|
||||
display: none;
|
||||
}
|
||||
.sidebar-footer {
|
||||
position: absolute;
|
||||
height: 40px;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
transition: all 0.6s ease 0s;
|
||||
text-align: center;
|
||||
}
|
||||
.sidebar-footer div a {
|
||||
color: #b2bfdc;
|
||||
font-size: 12px;
|
||||
line-height: 43px;
|
||||
}
|
||||
.sidebar-footer div a:hover {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* #592727 RED */
|
||||
/* #2f5927 GREEN */
|
||||
/* #30426a BLUE (default)*/
|
||||
/* Sidebar background color */
|
||||
/* Sidebar header and footer color */
|
||||
/* Sidebar title text colour */
|
||||
/* Sidebar menu item hover color */
|
||||
|
||||
/**
|
||||
* Widgets
|
||||
*/
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import angular from 'angular';
|
||||
|
||||
angular.module('portainer.azure').component('azureSidebarContent', {
|
||||
templateUrl: './azureSidebarContent.html',
|
||||
bindings: {
|
||||
endpointId: '<',
|
||||
},
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
<li class="sidebar-list">
|
||||
<a ui-sref="azure.dashboard({endpointId: $ctrl.endpointId})" ui-sref-active="active">Dashboard <span class="menu-icon fa fa-tachometer-alt fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="azure.containerinstances({endpointId: $ctrl.endpointId})" ui-sref-active="active">Container instances <span class="menu-icon fa fa-cubes fa-fw"></span></a>
|
||||
</li>
|
|
@ -0,0 +1,7 @@
|
|||
<sidebar-menu-item path="azure.dashboard" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-tachometer-alt fa-fw" class-name="sidebar-list">
|
||||
Dashboard
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="azure.containerinstances" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-cubes fa-fw" class-name="sidebar-list">
|
||||
Container instances
|
||||
</sidebar-menu-item>
|
|
@ -0,0 +1,8 @@
|
|||
import angular from 'angular';
|
||||
|
||||
angular.module('portainer.azure').component('azureSidebar', {
|
||||
templateUrl: './azure-sidebar.html',
|
||||
bindings: {
|
||||
endpointId: '<',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,121 @@
|
|||
<sidebar-menu-item path="docker.dashboard" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-tachometer-alt fa-fw" class-name="sidebar-list">
|
||||
Dashboard
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu
|
||||
ng-if="!$ctrl.offlineMode"
|
||||
label="App Templates"
|
||||
icon-class="fa-rocket fa-fw"
|
||||
path="docker.templates"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
is-sidebar-open="$ctrl.isSidebarOpen"
|
||||
children-paths="[]"
|
||||
>
|
||||
<sidebar-menu-item path="docker.templates.custom" path-params="{ endpointId: $ctrl.endpointId }" class-name="sidebar-sublist">
|
||||
Custom Templates
|
||||
</sidebar-menu-item>
|
||||
</sidebar-menu>
|
||||
|
||||
<sidebar-menu-item ng-if="$ctrl.showStacks" path="docker.stacks" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-th-list fa-fw" class-name="sidebar-list">
|
||||
Stacks
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item ng-if="$ctrl.swarmManagement" path="docker.services" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-list-alt fa-fw" class-name="sidebar-list">
|
||||
Services
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="docker.containers" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-cubes fa-fw" class-name="sidebar-list">
|
||||
Containers
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="docker.images" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-clone fa-fw" class-name="sidebar-list">
|
||||
Images
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="docker.networks" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-sitemap fa-fw" class-name="sidebar-list">
|
||||
Networks
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="docker.volumes" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-hdd fa-fw" class-name="sidebar-list">
|
||||
Volumes
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item
|
||||
ng-if="$ctrl.endpointApiVersion >= 1.3 && $ctrl.swarmManagement"
|
||||
path="docker.configs"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
icon-class="fa-file-code fa-fw"
|
||||
class-name="sidebar-list"
|
||||
>
|
||||
Configs
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item
|
||||
ng-if="$ctrl.endpointApiVersion >= 1.25 && $ctrl.swarmManagement"
|
||||
path="docker.secrets"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
icon-class="fa-user-secret fa-fw"
|
||||
class-name="sidebar-list"
|
||||
>
|
||||
Secrets
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item
|
||||
ng-if="$ctrl.standaloneManagement && $ctrl.adminAccess && !$ctrl.offlineMode"
|
||||
path="docker.events"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
icon-class="fa-history fa-fw"
|
||||
class-name="sidebar-list"
|
||||
>
|
||||
Events
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu
|
||||
ng-if="$ctrl.standaloneManagement"
|
||||
label="Host"
|
||||
icon-class="fa-th fa-fw"
|
||||
path="docker.host"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
is-sidebar-open="$ctrl.isSidebarOpen"
|
||||
children-paths="['docker.registries', 'docker.registries.access', 'docker.featuresConfiguration']"
|
||||
>
|
||||
<div ng-if="$ctrl.adminAccess">
|
||||
<sidebar-menu-item
|
||||
authorization="PortainerEndpointUpdateSettings"
|
||||
path="docker.featuresConfiguration"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
class-name="sidebar-sublist"
|
||||
>
|
||||
Setup
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item authorization="PortainerRegistryList" path="docker.registries" path-params="{ endpointId: $ctrl.endpointId }" class-name="sidebar-sublist">
|
||||
Registries
|
||||
</sidebar-menu-item>
|
||||
</div>
|
||||
</sidebar-menu>
|
||||
|
||||
<sidebar-menu
|
||||
ng-if="$ctrl.swarmManagement"
|
||||
label="Swarm"
|
||||
icon-class="fa-object-group fa-fw"
|
||||
path="docker.swarm"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
is-sidebar-open="$ctrl.isSidebarOpen"
|
||||
children-paths="['docker.registries', 'docker.registries.access', 'docker.featuresConfiguration']"
|
||||
>
|
||||
<div ng-if="$ctrl.adminAccess">
|
||||
<sidebar-menu-item
|
||||
authorization="PortainerEndpointUpdateSettings"
|
||||
path="docker.featuresConfiguration"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
class-name="sidebar-sublist"
|
||||
>
|
||||
Setup
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item authorization="PortainerRegistryList" path="docker.registries" path-params="{ endpointId: $ctrl.endpointId }" class-name="sidebar-sublist">
|
||||
Registries
|
||||
</sidebar-menu-item>
|
||||
</div>
|
||||
</sidebar-menu>
|
|
@ -1,12 +1,13 @@
|
|||
angular.module('portainer.docker').component('dockerSidebarContent', {
|
||||
templateUrl: './dockerSidebarContent.html',
|
||||
angular.module('portainer.docker').component('dockerSidebar', {
|
||||
templateUrl: './docker-sidebar.html',
|
||||
bindings: {
|
||||
isSidebarOpen: '<',
|
||||
|
||||
endpointApiVersion: '<',
|
||||
swarmManagement: '<',
|
||||
standaloneManagement: '<',
|
||||
adminAccess: '<',
|
||||
offlineMode: '<',
|
||||
toggle: '<',
|
||||
currentRouteName: '<',
|
||||
endpointId: '<',
|
||||
showStacks: '<',
|
|
@ -1,53 +0,0 @@
|
|||
<li class="sidebar-list">
|
||||
<a ui-sref="docker.dashboard({endpointId: $ctrl.endpointId})" ui-sref-active="active">Dashboard <span class="menu-icon fa fa-tachometer-alt fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="!$ctrl.offlineMode" authorization="DockerContainerCreate, PortainerStackCreate">
|
||||
<a ui-sref="docker.templates({endpointId: $ctrl.endpointId})" ui-sref-active="active">App Templates <span class="menu-icon fa fa-rocket fa-fw"></span></a>
|
||||
|
||||
<div class="sidebar-sublist" ng-if="$ctrl.toggle && $ctrl.currentRouteName.includes('docker.templates')">
|
||||
<a ui-sref="docker.templates.custom({endpointId: $ctrl.endpointId})" ui-sref-active="active">Custom Templates</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="$ctrl.showStacks">
|
||||
<a ui-sref="docker.stacks({endpointId: $ctrl.endpointId})" ui-sref-active="active">Stacks <span class="menu-icon fa fa-th-list fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="$ctrl.swarmManagement">
|
||||
<a ui-sref="docker.services({endpointId: $ctrl.endpointId})" ui-sref-active="active">Services <span class="menu-icon fa fa-list-alt fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="docker.containers({endpointId: $ctrl.endpointId})" ui-sref-active="active">Containers <span class="menu-icon fa fa-cubes fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="docker.images({endpointId: $ctrl.endpointId})" ui-sref-active="active">Images <span class="menu-icon fa fa-clone fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="docker.networks({endpointId: $ctrl.endpointId})" ui-sref-active="active">Networks <span class="menu-icon fa fa-sitemap fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="docker.volumes({endpointId: $ctrl.endpointId})" ui-sref-active="active">Volumes <span class="menu-icon fa fa-hdd fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="$ctrl.endpointApiVersion >= 1.3 && $ctrl.swarmManagement">
|
||||
<a ui-sref="docker.configs({endpointId: $ctrl.endpointId})" ui-sref-active="active">Configs <span class="menu-icon fa fa-file-code fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="$ctrl.endpointApiVersion >= 1.25 && $ctrl.swarmManagement">
|
||||
<a ui-sref="docker.secrets({endpointId: $ctrl.endpointId})" ui-sref-active="active">Secrets <span class="menu-icon fa fa-user-secret fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="$ctrl.standaloneManagement && $ctrl.adminAccess && !$ctrl.offlineMode">
|
||||
<a ui-sref="docker.events({endpointId: $ctrl.endpointId})" ui-sref-active="active">Events <span class="menu-icon fa fa-history fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ng-if="$ctrl.standaloneManagement" ui-sref="docker.host({endpointId: $ctrl.endpointId})" ui-sref-active="active">Host <span class="menu-icon fa fa-th fa-fw"></span></a>
|
||||
<a ng-if="$ctrl.swarmManagement" ui-sref="docker.swarm({endpointId: $ctrl.endpointId})" ui-sref-active="active">Swarm <span class="menu-icon fa fa-object-group fa-fw"></span></a>
|
||||
|
||||
<div
|
||||
ng-if="$ctrl.adminAccess && ['docker.swarm', 'docker.host', 'docker.registries', 'docker.registries.access', 'docker.featuresConfiguration'].includes($ctrl.currentRouteName)"
|
||||
>
|
||||
<div class="sidebar-sublist">
|
||||
<a ui-sref="docker.featuresConfiguration({endpointId: $ctrl.endpointId})" ui-sref-active="active">Setup</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-sublist">
|
||||
<a ui-sref="docker.registries({endpointId: $ctrl.endpointId})" ui-sref-active="active">Registries</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
|
@ -1,31 +0,0 @@
|
|||
<li class="sidebar-list">
|
||||
<a ui-sref="kubernetes.dashboard({endpointId: $ctrl.endpointId})" ui-sref-active="active">Dashboard <span class="menu-icon fa fa-tachometer-alt fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="kubernetes.resourcePools({endpointId: $ctrl.endpointId})" ui-sref-active="active">Namespaces <span class="menu-icon fa fa-layer-group fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="kubernetes.applications({endpointId: $ctrl.endpointId})" ui-sref-active="active">Applications <span class="menu-icon fa fa-laptop-code fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="kubernetes.configurations({endpointId: $ctrl.endpointId})" ui-sref-active="active">Configurations <span class="menu-icon fa fa-file-code fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="kubernetes.volumes({endpointId: $ctrl.endpointId})" ui-sref-active="active">Volumes <span class="menu-icon fa fa-database fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="kubernetes.cluster({endpointId: $ctrl.endpointId})" ui-sref-active="active">Cluster <span class="menu-icon fa fa-server fa-fw"></span></a>
|
||||
<div
|
||||
ng-if="
|
||||
$ctrl.adminAccess &&
|
||||
['kubernetes.cluster', 'portainer.endpoints.endpoint.kubernetesConfig', 'kubernetes.registries', 'kubernetes.registries.access'].includes($ctrl.currentState)
|
||||
"
|
||||
>
|
||||
<div class="sidebar-sublist">
|
||||
<a ui-sref="portainer.endpoints.endpoint.kubernetesConfig({id: $ctrl.endpointId})" ui-sref-active="active">Setup</a>
|
||||
</div>
|
||||
<div class="sidebar-sublist">
|
||||
<a ui-sref="kubernetes.registries({endpointId: $ctrl.endpointId})" ui-sref-active="active">Registries</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
|
@ -1,8 +0,0 @@
|
|||
angular.module('portainer.kubernetes').component('kubernetesSidebarContent', {
|
||||
templateUrl: './kubernetesSidebarContent.html',
|
||||
bindings: {
|
||||
adminAccess: '<',
|
||||
endpointId: '<',
|
||||
currentState: '<',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import angular from 'angular';
|
||||
|
||||
angular.module('portainer.kubernetes').component('kubernetesSidebar', {
|
||||
templateUrl: './kubernetes-sidebar.html',
|
||||
bindings: {
|
||||
endpointId: '<',
|
||||
isSidebarOpen: '<',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
<sidebar-menu-item path="kubernetes.dashboard" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-tachometer-alt fa-fw" class-name="sidebar-list">
|
||||
Dashboard
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="kubernetes.resourcePools" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-layer-group fa-fw" class-name="sidebar-list">
|
||||
Namespaces
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="kubernetes.applications" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-laptop-code fa-fw" class-name="sidebar-list">
|
||||
Applications
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="kubernetes.configurations" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-file-code fa-fw" class-name="sidebar-list">
|
||||
Configurations
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item path="kubernetes.volumes" path-params="{ endpointId: $ctrl.endpointId }" icon-class="fa-database fa-fw" class-name="sidebar-list">
|
||||
Volumes
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu
|
||||
icon-class="fa-server fa-fw"
|
||||
label="Cluster"
|
||||
path="kubernetes.cluster"
|
||||
path-params="{ endpointId: $ctrl.endpointId }"
|
||||
is-sidebar-open="$ctrl.isSidebarOpen"
|
||||
children-paths="['kubernetes.cluster', 'portainer.endpoints.endpoint.kubernetesConfig', 'kubernetes.registries', 'kubernetes.registries.access']"
|
||||
>
|
||||
<div ng-if="$ctrl.adminAccess">
|
||||
<sidebar-menu-item authorization="K8sClusterSetupRW" path="portainer.endpoints.endpoint.kubernetesConfig" path-params="{ id: $ctrl.endpointId }" class-name="sidebar-sublist">
|
||||
Setup
|
||||
</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu-item authorization="PortainerRegistryList" path="kubernetes.registries" path-params="{ endpointId: $ctrl.endpointId }" class-name="sidebar-sublist">
|
||||
Registries
|
||||
</sidebar-menu-item>
|
||||
</div>
|
||||
</sidebar-menu>
|
|
@ -1,7 +1,8 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import sidebarModule from './sidebar';
|
||||
import gitFormModule from './forms/git-form';
|
||||
import porAccessManagementModule from './accessManagement';
|
||||
import formComponentsModule from './form-components';
|
||||
|
||||
export default angular.module('portainer.app.components', [gitFormModule, porAccessManagementModule, formComponentsModule]).name;
|
||||
export default angular.module('portainer.app.components', [sidebarModule, gitFormModule, porAccessManagementModule, formComponentsModule]).name;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import angular from 'angular';
|
||||
import './sidebar.css';
|
||||
|
||||
import { sidebarMenu } from './sidebar-menu';
|
||||
import { sidebarSection } from './sidebar-section';
|
||||
import { sidebarMenuItem } from './sidebar-menu-item';
|
||||
|
||||
export default angular
|
||||
.module('portainer.app.components.sidebar', [])
|
||||
.component('sidebarMenu', sidebarMenu)
|
||||
.component('sidebarMenuItem', sidebarMenuItem)
|
||||
.component('sidebarSection', sidebarSection).name;
|
|
@ -0,0 +1,12 @@
|
|||
import './sidebar-menu-item.css';
|
||||
|
||||
export const sidebarMenuItem = {
|
||||
templateUrl: './sidebar-menu-item.html',
|
||||
bindings: {
|
||||
path: '@',
|
||||
pathParams: '<',
|
||||
iconClass: '@',
|
||||
className: '@',
|
||||
},
|
||||
transclude: true,
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
.sidebar-menu-item > a {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<li class="sidebar-menu-item" ng-class="$ctrl.className">
|
||||
<a ui-state="$ctrl.path" ui-state-params="$ctrl.pathParams" ui-sref-active="active">
|
||||
<ng-transclude></ng-transclude>
|
||||
<span ng-if="$ctrl.iconClass" class="menu-icon fa" ng-class="$ctrl.iconClass"></span>
|
||||
</a>
|
||||
</li>
|
|
@ -0,0 +1,18 @@
|
|||
import './sidebar-menu.css';
|
||||
|
||||
import controller from './sidebar-menu.controller.js';
|
||||
|
||||
export const sidebarMenu = {
|
||||
templateUrl: './sidebar-menu.html',
|
||||
controller,
|
||||
bindings: {
|
||||
iconClass: '@',
|
||||
path: '@', // string
|
||||
pathParams: '<', //object, corresponds to https://ui-router.github.io/ng1/docs/latest/modules/directives.html#uistatedirective
|
||||
childrenPaths: '<', // []string (globs)
|
||||
label: '@', // string
|
||||
isSidebarOpen: '<',
|
||||
currentState: '@',
|
||||
},
|
||||
transclude: true,
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
class SidebarMenuController {
|
||||
/* @ngInject */
|
||||
constructor($state) {
|
||||
this.$state = $state;
|
||||
|
||||
this.state = {
|
||||
forceOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
isOpen() {
|
||||
if (!this.isSidebarOpen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.state.forceOpen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isOpenByPathState();
|
||||
}
|
||||
|
||||
isOpenByPathState() {
|
||||
const currentName = this.$state.current.name;
|
||||
return currentName.startsWith(this.path) || this.childrenPaths.includes(currentName);
|
||||
}
|
||||
|
||||
onClickArrow(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
// prevent toggling when menu is open by state
|
||||
if (this.isOpenByPathState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.forceOpen = !this.state.forceOpen;
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.childrenPaths = this.childrenPaths || [];
|
||||
}
|
||||
}
|
||||
|
||||
export default SidebarMenuController;
|
|
@ -0,0 +1,15 @@
|
|||
.sidebar-menu .sidebar-menu-head {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-menu .sidebar-menu-head .sidebar-menu-indicator {
|
||||
background: none;
|
||||
border: 0;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
color: white;
|
||||
margin: 0 10px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
left: -5px;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<li class="sidebar-list sidebar-menu">
|
||||
<sidebar-menu-item path="{{::$ctrl.path }}" path-params="$ctrl.pathParams" icon-class="{{::$ctrl.iconClass}}">
|
||||
<div class="sidebar-menu-head">
|
||||
<button ng-click="$ctrl.onClickArrow($event)" class="small sidebar-menu-indicator">
|
||||
<i class="fas" ng-class="$ctrl.isOpen() ? 'fa-chevron-down' : 'fa-chevron-right'"></i>
|
||||
</button>
|
||||
{{ ::$ctrl.label }}
|
||||
</div>
|
||||
</sidebar-menu-item>
|
||||
|
||||
<div class="sidebar-list-items" ng-if="$ctrl.isOpen()">
|
||||
<ng-transclude></ng-transclude>
|
||||
</div>
|
||||
</li>
|
|
@ -0,0 +1,7 @@
|
|||
export const sidebarSection = {
|
||||
templateUrl: './sidebar-section.html',
|
||||
transclude: true,
|
||||
bindings: {
|
||||
title: '@',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
<div class="sidebar-section">
|
||||
<li class="sidebar-title">
|
||||
<span>{{ ::$ctrl.title }}</span>
|
||||
</li>
|
||||
|
||||
<div class="sidebar-section-items" ng-transclude> </div>
|
||||
</div>
|
|
@ -0,0 +1,326 @@
|
|||
#page-wrapper.open #sidebar-wrapper {
|
||||
left: 150px;
|
||||
}
|
||||
|
||||
/* #592727 RED */
|
||||
/* #2f5927 GREEN */
|
||||
/* #30426a BLUE (default)*/
|
||||
/* Sidebar background color */
|
||||
/* Sidebar header and footer color */
|
||||
/* Sidebar title text colour */
|
||||
/* Sidebar menu item hover color */
|
||||
/**
|
||||
* Sidebar
|
||||
*/
|
||||
#sidebar-wrapper {
|
||||
background: #30426a;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-main a,
|
||||
.sidebar-footer,
|
||||
ul.sidebar .sidebar-list a:hover,
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator {
|
||||
/* Sidebar header and footer color */
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
ul.sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
text-indent: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
ul.sidebar li a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-main {
|
||||
height: 65px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-main a {
|
||||
font-size: 18px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-main a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-main .menu-icon {
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
padding-right: 28px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title {
|
||||
color: #738bc0;
|
||||
font-size: 12px;
|
||||
height: 35px;
|
||||
line-height: 40px;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.6s ease 0s;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a {
|
||||
text-indent: 25px;
|
||||
font-size: 15px;
|
||||
color: #b2bfdc;
|
||||
line-height: 40px;
|
||||
padding-left: 5px;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a:hover {
|
||||
color: #fff;
|
||||
border-left-color: #e99d1a;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a:hover .menu-icon {
|
||||
text-indent: 25px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
padding-right: 30px;
|
||||
line-height: 40px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) ul.sidebar {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-title {
|
||||
display: none;
|
||||
height: 0px;
|
||||
text-indent: -100px;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator {
|
||||
display: block;
|
||||
height: 2px;
|
||||
margin: 13px 0;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) ul.sidebar .sidebar-list a:hover span {
|
||||
border-left: 3px solid #e99d1a;
|
||||
text-indent: 22px;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) .sidebar-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
position: absolute;
|
||||
height: 40px;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
transition: all 0.6s ease 0s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar-footer div a {
|
||||
color: #b2bfdc;
|
||||
font-size: 12px;
|
||||
line-height: 43px;
|
||||
}
|
||||
|
||||
.sidebar-footer div a:hover {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* #592727 RED */
|
||||
/* #2f5927 GREEN */
|
||||
/* #30426a BLUE (default)*/
|
||||
/* Sidebar background color */
|
||||
/* Sidebar header and footer color */
|
||||
/* Sidebar title text colour */
|
||||
/* Sidebar menu item hover color */
|
||||
|
||||
ul.sidebar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title.endpoint-name {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a.active {
|
||||
color: #fff;
|
||||
border-left-color: #fff;
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
height: 60px;
|
||||
list-style: none;
|
||||
text-indent: 20px;
|
||||
font-size: 18px;
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
.sidebar-header a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar-header a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-header .menu-icon {
|
||||
float: right;
|
||||
padding-right: 28px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
#page-wrapper:not(.open) .sidebar-footer-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-footer-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .logo {
|
||||
width: 100%;
|
||||
max-width: 100px;
|
||||
height: 100%;
|
||||
max-height: 35px;
|
||||
margin: 2px 0 2px 20px;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .update-notification {
|
||||
font-size: 14px;
|
||||
padding: 12px;
|
||||
border-radius: 2px;
|
||||
background-color: #ff851b;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .version {
|
||||
font-size: 11px;
|
||||
margin: 11px 20px 0 7px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar-footer-content .edition-version {
|
||||
font-size: 10px;
|
||||
margin-bottom: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#sidebar-wrapper {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.sidebar-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a.active .menu-icon {
|
||||
text-indent: 25px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
text-indent: 35px;
|
||||
font-size: 12px;
|
||||
color: #b2bfdc;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title .form-control {
|
||||
height: 36px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a,
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a.active {
|
||||
color: #fff;
|
||||
border-left: 3px solid #fff;
|
||||
background: #2d3e63;
|
||||
}
|
||||
|
||||
@media (max-height: 785px) {
|
||||
ul.sidebar .sidebar-title {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title .form-control {
|
||||
height: 26px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a,
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
font-size: 12px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
line-height: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-height: 786px) and (max-height: 924px) {
|
||||
ul.sidebar .sidebar-title {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-title .form-control {
|
||||
height: 30px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list a,
|
||||
ul.sidebar .sidebar-list .sidebar-sublist a {
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
ul.sidebar .sidebar-list .menu-icon {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
|
@ -9,193 +9,92 @@
|
|||
</div>
|
||||
<div class="sidebar-content">
|
||||
<ul class="sidebar">
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="portainer.home" ui-sref-active="active">Home <span class="menu-icon fa fa-home fa-fw"></span></a>
|
||||
</li>
|
||||
<sidebar-menu-item path="portainer.home" icon-class="fa-home fa-fw" class-name="sidebar-list">Home</sidebar-menu-item>
|
||||
|
||||
<li class="sidebar-title endpoint-name" ng-if="applicationState.endpoint.name">
|
||||
<span class="fa fa-plug space-right"></span>{{ applicationState.endpoint.name }}
|
||||
<kubectl-shell ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider === 'KUBERNETES'" class="kubectl-shell"></kubectl-shell>
|
||||
</li>
|
||||
<kubernetes-sidebar-content
|
||||
ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider === 'KUBERNETES'"
|
||||
admin-access="isAdmin"
|
||||
endpoint-id="endpointId"
|
||||
current-state="$state.current.name"
|
||||
>
|
||||
</kubernetes-sidebar-content>
|
||||
<azure-sidebar-content ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider === 'AZURE'" endpoint-id="endpointId"> </azure-sidebar-content>
|
||||
<docker-sidebar-content
|
||||
ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider !== 'AZURE' && applicationState.endpoint.mode.provider !== 'KUBERNETES'"
|
||||
current-route-name="$state.current.name"
|
||||
toggle="toggle"
|
||||
show-stacks="showStacks"
|
||||
endpoint-api-version="applicationState.endpoint.apiVersion"
|
||||
swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'"
|
||||
standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'"
|
||||
admin-access="isAdmin"
|
||||
offline-mode="endpointState.OfflineMode"
|
||||
endpoint-id="endpointId"
|
||||
></docker-sidebar-content>
|
||||
<li class="sidebar-title" authorization="IntegrationStoridgeAdmin" ng-if="applicationState.endpoint.mode && applicationState.endpoint.extensions.length > 0">
|
||||
<span>Integrations</span>
|
||||
</li>
|
||||
<li
|
||||
authorization="IntegrationStoridgeAdmin"
|
||||
class="sidebar-list"
|
||||
ng-if="
|
||||
applicationState.endpoint.mode &&
|
||||
applicationState.endpoint.extensions.indexOf('storidge') !== -1 &&
|
||||
applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' &&
|
||||
applicationState.endpoint.mode.role === 'MANAGER'
|
||||
"
|
||||
>
|
||||
<a ui-sref="storidge.cluster" ui-sref-active="active">Storidge <span class="menu-icon fa fa-bolt fa-fw"></span></a>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
<div ng-if="applicationState.endpoint.mode">
|
||||
<kubernetes-sidebar ng-if="applicationState.endpoint.mode.provider === 'KUBERNETES'" is-sidebar-open="toggle" endpoint-id="endpointId"></kubernetes-sidebar>
|
||||
|
||||
<azure-sidebar ng-if="applicationState.endpoint.mode.provider === 'AZURE'" endpoint-id="endpointId"> </azure-sidebar>
|
||||
|
||||
<docker-sidebar
|
||||
ng-if="applicationState.endpoint.mode.provider !== 'AZURE' && applicationState.endpoint.mode.provider !== 'KUBERNETES'"
|
||||
current-route-name="$state.current.name"
|
||||
is-sidebar-open="toggle"
|
||||
show-stacks="showStacks"
|
||||
endpoint-api-version="applicationState.endpoint.apiVersion"
|
||||
swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'"
|
||||
standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'"
|
||||
admin-access="isAdmin || isEndpointAdmin"
|
||||
offline-mode="endpointState.OfflineMode"
|
||||
endpoint-id="endpointId"
|
||||
></docker-sidebar>
|
||||
</div>
|
||||
|
||||
<sidebar-section title="Integrations" ng-if="applicationState.endpoint.mode && applicationState.endpoint.extensions.length > 0" authorization="IntegrationStoridgeAdmin">
|
||||
<sidebar-menu
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'storidge.cluster' ||
|
||||
$state.current.name === 'storidge.profiles' ||
|
||||
$state.current.name === 'storidge.monitor' ||
|
||||
$state.current.name === 'storidge.profiles.new' ||
|
||||
$state.current.name === 'storidge.profiles.profile' ||
|
||||
$state.current.name === 'storidge.drives' ||
|
||||
$state.current.name === 'storidge.drives.drive' ||
|
||||
$state.current.name === 'storidge.cluster.node')
|
||||
applicationState.endpoint.mode &&
|
||||
applicationState.endpoint.extensions.indexOf('storidge') !== -1 &&
|
||||
applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' &&
|
||||
applicationState.endpoint.mode.role === 'MANAGER'
|
||||
"
|
||||
icon-class="fa-bolt fa-fw"
|
||||
label="Storidge"
|
||||
path="storidge.cluster"
|
||||
is-sidebar-open="toggle"
|
||||
children-paths="['storidge.cluster', 'storidge.profiles', 'storidge.monitor', 'storidge.profiles.new', 'storidge.profiles.profile', 'storidge.drives', 'storidge.drives.drive', 'storidge.cluster.node']"
|
||||
>
|
||||
<a ui-sref="storidge.monitor" ui-sref-active="active">Monitor</a>
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'storidge.cluster' ||
|
||||
$state.current.name === 'storidge.profiles' ||
|
||||
$state.current.name === 'storidge.monitor' ||
|
||||
$state.current.name === 'storidge.profiles.new' ||
|
||||
$state.current.name === 'storidge.profiles.profile' ||
|
||||
$state.current.name === 'storidge.drives' ||
|
||||
$state.current.name === 'storidge.drives.drive' ||
|
||||
$state.current.name === 'storidge.cluster.node')
|
||||
"
|
||||
<sidebar-menu-item path="storidge.monitor" class-name="sidebar-sublist">Monitor</sidebar-menu-item>
|
||||
<sidebar-menu-item path="storidge.profiles" class-name="sidebar-sublist">Profiles</sidebar-menu-item>
|
||||
<sidebar-menu-item path="storidge.drives" class-name="sidebar-sublist">Drives</sidebar-menu-item>
|
||||
</sidebar-menu>
|
||||
</sidebar-section>
|
||||
|
||||
<sidebar-section title="Edge compute" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
|
||||
<sidebar-menu-item path="edge.groups" icon-class="fa-object-group fa-fw" class-name="sidebar-list">Edge Groups</sidebar-menu-item>
|
||||
<sidebar-menu-item path="edge.stacks" icon-class="fa-layer-group fa-fw" class-name="sidebar-list">Edge Stacks</sidebar-menu-item>
|
||||
<sidebar-menu-item path="edge.jobs" icon-class="fa-clock fa-fw" class-name="sidebar-list">Edge Jobs</sidebar-menu-item>
|
||||
</sidebar-section>
|
||||
|
||||
<sidebar-section ng-if="isAdmin || isTeamLeader" title="Settings">
|
||||
<sidebar-menu
|
||||
icon-class="fa-users fa-fw"
|
||||
label="Users"
|
||||
path="portainer.users"
|
||||
is-sidebar-open="toggle"
|
||||
children-paths="['portainer.users.user' ,'portainer.teams' ,'portainer.teams.team' ,'portainer.roles' ,'portainer.roles.role' ,'portainer.roles.new']"
|
||||
>
|
||||
<a ui-sref="storidge.profiles" ui-sref-active="active">Profiles</a>
|
||||
<sidebar-menu-item path="portainer.teams" class-name="sidebar-sublist">Teams</sidebar-menu-item>
|
||||
<sidebar-menu-item path="portainer.roles" class-name="sidebar-sublist">Roles</sidebar-menu-item>
|
||||
</sidebar-menu>
|
||||
|
||||
<div ng-if="isAdmin">
|
||||
<sidebar-menu
|
||||
icon-class="fa-plug fa-fw"
|
||||
label="Endpoints"
|
||||
path="portainer.endpoints"
|
||||
is-sidebar-open="toggle"
|
||||
children-paths="['portainer.endpoints.endpoint', 'portainer.endpoints.new', 'portainer.endpoints.endpoint.access', 'portainer.groups', 'portainer.groups.group', 'portainer.groups.group.access', 'portainer.groups.new', 'portainer.tags']"
|
||||
>
|
||||
<sidebar-menu-item path="portainer.groups" class-name="sidebar-sublist">Groups</sidebar-menu-item>
|
||||
<sidebar-menu-item path="portainer.tags" class-name="sidebar-sublist">Tags</sidebar-menu-item>
|
||||
</sidebar-menu>
|
||||
|
||||
<sidebar-menu-item path="portainer.registries" icon-class="fa-database fa-fw" class-name="sidebar-list">Registries</sidebar-menu-item>
|
||||
|
||||
<sidebar-menu label="Settings" icon-class="fa-cogs fa-fw" path="portainer.settings" is-sidebar-open="toggle" children-paths="['portainer.settings.authentication']">
|
||||
<sidebar-menu-item path="portainer.settings.authentication" class-name="sidebar-sublist">Authentication</sidebar-menu-item>
|
||||
|
||||
<div class="sidebar-sublist">
|
||||
<a href="http://www.portainer.io/help_about" target="_blank">Help / About</a>
|
||||
</div>
|
||||
</sidebar-menu>
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'storidge.cluster' ||
|
||||
$state.current.name === 'storidge.profiles' ||
|
||||
$state.current.name === 'storidge.monitor' ||
|
||||
$state.current.name === 'storidge.profiles.new' ||
|
||||
$state.current.name === 'storidge.profiles.profile' ||
|
||||
$state.current.name === 'storidge.drives' ||
|
||||
$state.current.name === 'storidge.drives.drive' ||
|
||||
$state.current.name === 'storidge.cluster.node')
|
||||
"
|
||||
>
|
||||
<a ui-sref="storidge.drives" ui-sref-active="active">Drives</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar-title" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
|
||||
<span>Edge Compute</span>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
|
||||
<a ui-sref="edge.groups" ui-sref-active="active">Edge Groups <span class="menu-icon fa fa-object-group fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
|
||||
<a ui-sref="edge.stacks" ui-sref-active="active">Edge Stacks <span class="menu-icon fa fa-layer-group fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
|
||||
<a ui-sref="edge.jobs" ui-sref-active="active">Edge Jobs <span class="menu-icon fa fa-clock fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-title" ng-if="isAdmin || isTeamLeader">
|
||||
<span>Settings</span>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin || isTeamLeader">
|
||||
<a ui-sref="portainer.users" ui-sref-active="active">Users <span class="menu-icon fa fa-users fa-fw"></span></a>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'portainer.users' ||
|
||||
$state.current.name === 'portainer.users.user' ||
|
||||
$state.current.name === 'portainer.teams' ||
|
||||
$state.current.name === 'portainer.teams.team' ||
|
||||
$state.current.name === 'portainer.roles')
|
||||
"
|
||||
>
|
||||
<a ui-sref="portainer.teams" ui-sref-active="active">Teams</a>
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'portainer.users' ||
|
||||
$state.current.name === 'portainer.users.user' ||
|
||||
$state.current.name === 'portainer.teams' ||
|
||||
$state.current.name === 'portainer.teams.team' ||
|
||||
$state.current.name === 'portainer.roles')
|
||||
"
|
||||
>
|
||||
<a ui-sref="portainer.roles" ui-sref-active="active">Roles</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin">
|
||||
<a
|
||||
ui-sref="portainer.endpoints"
|
||||
ng-class="{ active: $state.current.name.includes('portainer.endpoints') && $state.current.name !== 'portainer.endpoints.endpoint.kubernetesConfig' }"
|
||||
>Endpoints <span class="menu-icon fa fa-plug fa-fw"></span
|
||||
></a>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'portainer.endpoints' ||
|
||||
$state.current.name === 'portainer.endpoints.endpoint' ||
|
||||
$state.current.name === 'portainer.endpoints.new' ||
|
||||
$state.current.name === 'portainer.endpoints.endpoint.access' ||
|
||||
$state.current.name === 'portainer.groups' ||
|
||||
$state.current.name === 'portainer.groups.group' ||
|
||||
$state.current.name === 'portainer.groups.group.access' ||
|
||||
$state.current.name === 'portainer.groups.new' ||
|
||||
$state.current.name === 'portainer.tags')
|
||||
"
|
||||
>
|
||||
<a ui-sref="portainer.groups" ui-sref-active="active">Groups</a>
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-sublist"
|
||||
ng-if="
|
||||
toggle &&
|
||||
($state.current.name === 'portainer.endpoints' ||
|
||||
$state.current.name === 'portainer.endpoints.endpoint' ||
|
||||
$state.current.name === 'portainer.endpoints.new' ||
|
||||
$state.current.name === 'portainer.endpoints.endpoint.access' ||
|
||||
$state.current.name === 'portainer.groups' ||
|
||||
$state.current.name === 'portainer.groups.group' ||
|
||||
$state.current.name === 'portainer.groups.group.access' ||
|
||||
$state.current.name === 'portainer.groups.new' ||
|
||||
$state.current.name === 'portainer.tags')
|
||||
"
|
||||
>
|
||||
<a ui-sref="portainer.tags" ui-sref-active="active">Tags</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin">
|
||||
<a ui-sref="portainer.registries" ui-sref-active="active">Registries <span class="menu-icon fa fa-database fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="isAdmin">
|
||||
<a ui-sref="portainer.settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs fa-fw"></span></a>
|
||||
<div class="sidebar-sublist" ng-if="toggle && $state.current.name.startsWith('portainer.settings') && isAdmin">
|
||||
<a ui-sref="portainer.settings.authentication" ui-sref-active="active">Authentication</a>
|
||||
</div>
|
||||
<div class="sidebar-sublist" ng-if="toggle && $state.current.name.startsWith('portainer.settings')">
|
||||
<a href="http://www.portainer.io/help_about" target="_blank">Help / About</a>
|
||||
</div>
|
||||
</li>
|
||||
</sidebar-section>
|
||||
</ul>
|
||||
<div class="sidebar-footer-content">
|
||||
<div class="update-notification" ng-if="applicationState.application.versionStatus.UpdateAvailable">
|
||||
|
|
|
@ -1,71 +1,66 @@
|
|||
angular.module('portainer.app').controller('SidebarController', [
|
||||
'$rootScope',
|
||||
'$q',
|
||||
'$scope',
|
||||
'$transitions',
|
||||
'StateManager',
|
||||
'Notifications',
|
||||
'Authentication',
|
||||
'UserService',
|
||||
'EndpointProvider',
|
||||
function ($rootScope, $q, $scope, $transitions, StateManager, Notifications, Authentication, UserService, EndpointProvider) {
|
||||
function checkPermissions(memberships) {
|
||||
var isLeader = false;
|
||||
angular.forEach(memberships, function (membership) {
|
||||
if (membership.Role === 1) {
|
||||
isLeader = true;
|
||||
}
|
||||
});
|
||||
$scope.isTeamLeader = isLeader;
|
||||
}
|
||||
angular.module('portainer.app').controller('SidebarController', SidebarController);
|
||||
|
||||
function isClusterAdmin() {
|
||||
return Authentication.isAdmin();
|
||||
}
|
||||
function SidebarController($rootScope, $scope, $transitions, StateManager, Notifications, Authentication, UserService, EndpointProvider) {
|
||||
$scope.applicationState = StateManager.getState();
|
||||
$scope.endpointState = EndpointProvider.endpoint();
|
||||
|
||||
async function initView() {
|
||||
$scope.uiVersion = StateManager.getState().application.version;
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
|
||||
$scope.endpointId = EndpointProvider.endpointID();
|
||||
$scope.showStacks = shouldShowStacks();
|
||||
|
||||
let userDetails = Authentication.getUserDetails();
|
||||
const isAdmin = isClusterAdmin();
|
||||
$scope.isAdmin = isAdmin;
|
||||
|
||||
$q.when(!isAdmin ? UserService.userMemberships(userDetails.ID) : [])
|
||||
.then(function success(data) {
|
||||
checkPermissions(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve user memberships');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
|
||||
function shouldShowStacks() {
|
||||
if (isClusterAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const endpoint = EndpointProvider.currentEndpoint();
|
||||
if (!endpoint || !endpoint.SecuritySettings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return endpoint.SecuritySettings.allowStackManagementForRegularUsers;
|
||||
}
|
||||
|
||||
$transitions.onEnter({}, async () => {
|
||||
$scope.endpointId = EndpointProvider.endpointID();
|
||||
$scope.showStacks = shouldShowStacks();
|
||||
$scope.isAdmin = isClusterAdmin();
|
||||
|
||||
if ($scope.applicationState.endpoint.name) {
|
||||
document.title = `${$rootScope.defaultTitle} | ${$scope.applicationState.endpoint.name}`;
|
||||
function checkPermissions(memberships) {
|
||||
var isLeader = false;
|
||||
angular.forEach(memberships, function (membership) {
|
||||
if (membership.Role === 1) {
|
||||
isLeader = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
]);
|
||||
$scope.isTeamLeader = isLeader;
|
||||
}
|
||||
|
||||
function isClusterAdmin() {
|
||||
return Authentication.isAdmin();
|
||||
}
|
||||
|
||||
async function initView() {
|
||||
$scope.uiVersion = StateManager.getState().application.version;
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
|
||||
$scope.endpointId = EndpointProvider.endpointID();
|
||||
$scope.showStacks = shouldShowStacks();
|
||||
|
||||
const userDetails = Authentication.getUserDetails();
|
||||
const isAdmin = isClusterAdmin();
|
||||
$scope.isAdmin = isAdmin;
|
||||
|
||||
if (!isAdmin) {
|
||||
try {
|
||||
const memberships = await UserService.userMemberships(userDetails.ID);
|
||||
checkPermissions(memberships);
|
||||
} catch (err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve user memberships');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initView();
|
||||
|
||||
function shouldShowStacks() {
|
||||
if (isClusterAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const endpoint = EndpointProvider.currentEndpoint();
|
||||
if (!endpoint || !endpoint.SecuritySettings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return endpoint.SecuritySettings.allowStackManagementForRegularUsers;
|
||||
}
|
||||
|
||||
$transitions.onEnter({}, async () => {
|
||||
$scope.endpointId = EndpointProvider.endpointID();
|
||||
$scope.showStacks = shouldShowStacks();
|
||||
$scope.isAdmin = isClusterAdmin();
|
||||
|
||||
if ($scope.applicationState.endpoint.name) {
|
||||
document.title = `${$rootScope.defaultTitle} | ${$scope.applicationState.endpoint.name}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue